Make a clean environment

 rm(list=ls())


Load packages

packages.list <- c("ggplot2","treeio","ggtree","ggnewscale","ape","dplyr","tidyverse","tidyr","phytools","RColorBrewer","lubridate","readxl","ggforce","ggstance","ggridges","cowplot","hexbin","scales","haven","network","ggnetwork","intergraph","igraph","ggraph","graphlayouts","scatterpie","maps","mapdata","maptools","rgdal","rgeos","broom","ggrepel","ggridges","magick","ggbeeswarm")

#"plyr","Cairo","ggmap","emojifont","rPinecone","pairsnp","CoordinateCleaner","gridExtra","dendextend","ggdendro",

#BiocManager::install("ggtree")
#BiocManager::install("treeio")

for(pkg in packages.list){
  eval(bquote(library(.(pkg)))) }


Confirm current environmental setup

R.Version()
$platform
[1] "x86_64-apple-darwin17.0"

$arch
[1] "x86_64"

$os
[1] "darwin17.0"

$system
[1] "x86_64, darwin17.0"

$status
[1] ""

$major
[1] "4"

$minor
[1] "1.2"

$year
[1] "2021"

$month
[1] "11"

$day
[1] "01"

$`svn rev`
[1] "81115"

$language
[1] "R"

$version.string
[1] "R version 4.1.2 (2021-11-01)"

$nickname
[1] "Bird Hippie"
print(sessionInfo())
R version 4.1.2 (2021-11-01)
Platform: x86_64-apple-darwin17.0 (64-bit)
Running under: macOS Monterey 12.6.1

Matrix products: default
LAPACK: /Library/Frameworks/R.framework/Versions/4.1/Resources/lib/libRlapack.dylib

locale:
[1] en_GB.UTF-8/en_GB.UTF-8/en_GB.UTF-8/C/en_GB.UTF-8/en_GB.UTF-8

attached base packages:
[1] stats     graphics  grDevices utils     datasets  methods   base     

other attached packages:
 [1] magick_2.7.3       ggrepel_0.9.1      broom_1.0.0        rgeos_0.5-9        rgdal_1.5-30      
 [6] maptools_1.1-4     mapdata_2.3.0      scatterpie_0.1.7   graphlayouts_0.7.2 ggraph_2.0.5      
[11] igraph_1.3.5       intergraph_2.0-2   ggnetwork_0.5.10   network_1.17.1     haven_2.4.3       
[16] scales_1.2.1       hexbin_1.28.2      ggridges_0.5.3     ggstance_0.3.5     ggforce_0.3.3     
[21] readxl_1.3.1       lubridate_1.8.0    RColorBrewer_1.1-3 ggnewscale_0.4.5   treeio_1.18.1     
[26] phytools_0.7-90    maps_3.4.0         phangorn_2.8.0     ggtree_3.2.1       ape_5.6-2         
[31] vcfR_1.13.0        cowplot_1.1.1      ggbeeswarm_0.6.0   forcats_0.5.1      stringr_1.4.1     
[36] purrr_0.3.4        readr_2.1.3        tidyr_1.1.4        tibble_3.1.8       ggplot2_3.3.6     
[41] tidyverse_1.3.2    dplyr_1.0.7        sp_1.4-6           Matrix_1.3-4      

loaded via a namespace (and not attached):
  [1] backports_1.4.0         Hmisc_4.7-0             fastmatch_1.1-3         plyr_1.8.6             
  [5] lazyeval_0.2.2          splines_4.1.2           digest_0.6.30           yulab.utils_0.0.4      
  [9] htmltools_0.5.3         viridis_0.6.2           fansi_1.0.3             magrittr_2.0.3         
 [13] checkmate_2.1.0         googlesheets4_1.0.0     cluster_2.1.2           tzdb_0.3.0             
 [17] sna_2.6                 modelr_0.1.8            jpeg_0.1-9              colorspace_2.0-3       
 [21] rvest_1.0.2             xfun_0.34               crayon_1.5.2            jsonlite_1.8.3         
 [25] survival_3.2-13         glue_1.6.2              polyclip_1.10-0         gtable_0.3.1           
 [29] gargle_1.2.0            seqinr_4.2-16           BiocGenerics_0.40.0     DBI_1.1.1              
 [33] Rcpp_1.0.9              plotrix_3.8-2           viridisLite_0.4.1       htmlTable_2.4.1        
 [37] tmvnsim_1.0-2           gridGraphics_0.5-1      tidytree_0.3.6          foreign_0.8-81         
 [41] Formula_1.2-4           stats4_4.1.2            htmlwidgets_1.5.4       httr_1.4.4             
 [45] geosphere_1.5-14        ellipsis_0.3.2          pkgconfig_2.0.3         farver_2.1.1           
 [49] nnet_7.3-16             sass_0.4.2              dbplyr_2.1.1            deldir_1.0-6           
 [53] utf8_1.2.2              ggplotify_0.1.0         tidyselect_1.2.0        labeling_0.4.2         
 [57] rlang_1.0.6             reshape2_1.4.4          munsell_0.5.0           cellranger_1.1.0       
 [61] tools_4.1.2             cachem_1.0.6            cli_3.4.1               generics_0.1.1         
 [65] statnet.common_4.5.0    ggmap_3.0.0             ade4_1.7-19             evaluate_0.17          
 [69] fastmap_1.1.0           yaml_2.3.6              knitr_1.40              fs_1.5.2               
 [73] tidygraph_1.2.0         RgoogleMaps_1.4.5.3     nlme_3.1-153            RcppRoll_0.3.0         
 [77] aplot_0.1.1             xml2_1.3.3              compiler_4.1.2          rstudioapi_0.13        
 [81] beeswarm_0.4.0          png_0.1-7               clusterGeneration_1.3.7 reprex_2.0.1           
 [85] tweenr_1.0.2            bslib_0.4.0             stringi_1.7.8           memuse_4.2-1           
 [89] lattice_0.20-45         vegan_2.6-2             permute_0.9-7           vctrs_0.5.0            
 [93] pillar_1.8.1            lifecycle_1.0.3         combinat_0.0-8          jquerylib_0.1.4        
 [97] bitops_1.0-7            data.table_1.14.2       patchwork_1.1.1         R6_2.5.1               
[101] latticeExtra_0.6-30     gridExtra_2.3           vipor_0.4.5             IRanges_2.28.0         
[105] codetools_0.2-18        MASS_7.3-54             assertthat_0.2.1        rjson_0.2.20           
[109] withr_2.5.0             pinfsc50_1.2.0          mnormt_2.0.2            S4Vectors_0.32.4       
[113] expm_0.999-6            mgcv_1.8-38             parallel_4.1.2          hms_1.1.2              
[117] fastbaps_1.0.6          quadprog_1.5-8          grid_4.1.2              pairsnp_0.1.0          
[121] rpart_4.1-15            ggfun_0.0.4             coda_0.19-4             rmarkdown_2.17         
[125] googledrive_2.0.0       Cairo_1.5-12.2          scatterplot3d_0.3-41    numDeriv_2016.8-1.1    
[129] base64enc_0.1-3         interp_1.1-3           


Make some shortcuts for plotting

y.theme.strip <- theme(axis.title.y = element_blank(), axis.text.y = element_blank(), axis.ticks.y= element_blank())
y.theme.strip.partial <- theme(axis.text.y = element_blank(), axis.ticks.y= element_blank())

x.theme.strip <- theme(axis.title.x = element_blank(), axis.text.x = element_blank(), axis.ticks.x= element_blank())
x.theme.strip.partial <- theme(axis.text.x = element_blank(), axis.ticks.x= element_blank())
x.theme.strip.labs <- theme(axis.text.x = element_blank(),axis.title.x = element_blank())

x.theme.axis.rotate <- theme(axis.text.x = element_text(angle = 90, vjust = 0.5, hjust=1))

legend.strip <- theme(legend.position = "none")

theme.text.size <- theme(text = element_text(size = 10))

'%notin%' <- Negate('%in%')

max.font.size <- 7
basic.font.size <- 6
min.font.size <- 5.25
theme.text.size <- theme(text = element_text(size = basic.font.size))
theme.text.size.within <- (5/14)*min.font.size
panel.lab.size <- 10


Specify raw data - global dataset

#Data_input_directory <- "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Data/"
#Data_input_directory <- "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Rnotebook/Rnotebook_09-2022/data/"
Data_input_directory <- paste0(getwd(), "/inputdata/")


################################
#### Tree data 

# ML tree (refined dataset)
TPA.MLtree.file <- paste0(Data_input_directory,"TPA-uber.remasked.2020-11-10.goodcov25.gubbins.SNPs.aln.renamed.fix-zero-dist.treefile")

# Pyjar tree (refined dataset)
TPA.pyjar.file <- paste0(Data_input_directory,"TPA-uber.remasked.2020-11-10.goodcov25.gubbins.SNPs.aln.renamed.pyjar.tre")

# Full size BEAST2 analysis - previously generated as part of Beale, 2021.
full.beast2.tree.file <- paste0(Data_input_directory,"TPA-uber_beast2_strict-skyline-500M_10pop_consensus.tree")


################################
#### Meta data 

# Supplement from TPA-Uber paper - Beale, 2021 
TPA.meta2.file <- paste0(Data_input_directory,"Sup_Data1_Global_Sample-Metadata__09-2022.xlsx")

# England specific metadata collated by PHE/UKHSA
PHE.metadata.linked.file <- paste0(Data_input_directory,"Sup_Data2_TPA.UK-only.PHE.metadata.2022-02-02.xlsx")

# England specific mapping shapefile data with Public Health Boundaries
# Imported datafile from https://geoportal.statistics.gov.uk/datasets/public-health-england-centres-december-2016-full-clipped-boundaries-in-england/explore?location=52.950000%2C-2.000000%2C6.88
UK.publichealth.shapefile.data <- paste0(Data_input_directory,"Public_Health_England_Centres_(December_2016)_Boundaries")


################################
#### Externally plotted figures (e.g. GrapeTree) for inclusion in multipanel figures

# Externally plotted grapetree minimum spanning tree for whole of England - code to extract subtree that was used to make this is included later in this Rnotebook
TPA.UK.Grapetree.sublineages.file <- paste0(Data_input_directory,"TPA-UK-2022-02-03.sublineage-MSTree.Inkscaped.svg")

# Externally plotted grapetree minimum spanning tree for whole of England - 3-variable plots
TPA.UK.Grapetree.3way.file <- paste0(Data_input_directory,"TPA-UK-2022-02-16.-MSTree_3-way-figure.Inscaped-3.svg")

# Externally plotted grapetree minimum spanning tree for whole of England - HIV status
TPA.UK.Grapetree.HIV.file <- paste0(Data_input_directory,"TPA-UK-2022-02-03.HIVstatus-MSTree_inkscaped.svg")

# Externally plotted grapetree minimum spanning tree for North East England networks
TPA.NorthEastEngland.Grapetree.file <- paste0(Data_input_directory,"TPA-UK-NorthEast-2022-02-26.GenderOrientation-MSTree.inkscaped.+node-counts+GBMSM.svg")


Specify directory to output plots

Figure_output_directory <- paste0(getwd(), "/Figures/")

#"/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Figures/Figure_Drafting/Working_Figures_08-2022/"


Read in trees

TPA.MLtree <- midpoint.root(read.tree(TPA.MLtree.file))
TPA.pyjar.tree <- midpoint.root(read.tree(TPA.pyjar.file))


Read in final output metadata from Global Uber study (Beale 2021)

TPA.meta2.1 <- readxl::read_excel(TPA.meta2.file,sheet="Supplementary_Data1_Sample-Meta")


Create a colour scheme for Lineages, Countries and Continents (consistent with Beale, 2021)

# Colouring for country
continental.country.cols.brew2 <- unique(TPA.meta2.1[,c("Geo_Country","Continent")])
continental.country.cols.brew2 <- continental.country.cols.brew2[order(continental.country.cols.brew2$Continent,continental.country.cols.brew2$Geo_Country),]

continental.country.cols.brew2$country.col <- c("#ec7014","#fec44f","#de2d26","#fb6a4a","#bdbdbd","#737373",brewer.pal(n=8,"Purples")[4:8],brewer.pal(n=8,"Blues")[3:8],brewer.pal(n=5,"Greens")[3:5],"#c51b8a","#8c510a")

# Colouring for Continent
continental.cols.brew2 <- data.frame(Continent=sort(unique(TPA.meta2.1$Continent)),stringsAsFactors=F)
continental.cols.brew2$continent.col <- c("#fec44f","#de2d26","#bdbdbd","#2171b5","#74c476","#c51b8a","#ec7014")


# Colouring for TPA Lineage
TPA_Lineage.cols <- data.frame(Lineage=sort(unique(TPA.meta2.1$TPA_Lineage)),stringsAsFactors=F)
TPA_Lineage.cols$Lineage.col <- c("royalblue2", "indianred1")
#c("#436eee", "#666666","#ff6a6a")
TPA_Lineage.cols$Lineage <- factor(TPA_Lineage.cols$Lineage, levels=c("Nichols","SS14","outlier"))

# Lineage Hexcodes
# royalblue2 #436eee
# indianred1 #ff6a6a


Define colours for sublineages

# Define sublineage clustering scheme using brew colourscales
sublineages.cols.brew <- data.frame(unique(TPA.meta2.1[,c("TPA_Lineage","TPA.pinecone.sublineage")]), stringsAsFactors = F)
sublineages.cols.brew <- sublineages.cols.brew[order(sublineages.cols.brew$TPA_Lineage,sublineages.cols.brew$TPA.pinecone.sublineage),]

sublineages.cols.brew$sublin.order <- as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))
Warning: NAs introduced by coercion
sublineages.cols.brew <- sublineages.cols.brew[order(sublineages.cols.brew$sublin.order),]

# For revised bootstrapped clusters
sublineages.cols.brew$sublineage.cols <- c("#FC9272","#EF3B2C",brewer.pal(n=4,"Greens")[2:4],brewer.pal(n=4,"YlOrBr")[c(2,3)],brewer.pal(n=6,"Blues")[2:6],brewer.pal(n=6,"Purples")[2:6],"grey80","grey80","grey80","grey80")
  
sublineages.cols.brew <- unique(sublineages.cols.brew[,c("TPA.pinecone.sublineage","sublineage.cols")])
sublineages.cols.brew <- sublineages.cols.brew[order(as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))),]
Warning in order(as.numeric(as.character(sublineages.cols.brew$TPA.pinecone.sublineage))) :
  NAs introduced by coercion
sublineages.cols.brew$TPA.pinecone.sublineage <- factor(sublineages.cols.brew$TPA.pinecone.sublineage, levels=sublineages.cols.brew$TPA.pinecone.sublineage)
sublineages.cols.brew <- sublineages.cols.brew[!is.na(sublineages.cols.brew$sublineage),]

colnames(sublineages.cols.brew) <- c("sublineage","sublineage.cols")
sublineages.cols.brew <- unique(sublineages.cols.brew)


Restrict analysis to high quality genomes (and tree)

TPA.meta2.1 <- TPA.meta2.1[TPA.meta2.1$finescale.analysis=="Yes",]


Create a “UK” variable, and a “PHE” variable

TPA.meta2.1$is.UK <- ifelse(TPA.meta2.1$Geo_Country=="UK","UK","Other")
TPA.meta2.1$is.PHE <- ifelse(TPA.meta2.1$Geo_Country=="UK" & grepl("PHE",TPA.meta2.1$Sample_Name),"PHE","Other")


# Prepare ML tree
TPA.MLtree.ggtree <- ggtree(TPA.MLtree,layout = "fan",open.angle = 10, right=T)
Scale for 'y' is already present. Adding another scale for 'y', which will replace the existing
scale.
# Prepare country dataset
TPA.rawseq.countries.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Country=TPA.meta2.1$Geo_Country, stringsAsFactors = F)

# Prepare continent dataset
TPA.rawseq.continents.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Continent=TPA.meta2.1$Continent, stringsAsFactors = F)

# Prepare UK data strip
TPA.rawseq.UK.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, England=TPA.meta2.1$is.UK, stringsAsFactors = F)
TPA.rawseq.UK.p[TPA.rawseq.UK.p$England=="UK",] <- "England"

# Prepare PHE data strip
TPA.rawseq.PHE.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, PHE=TPA.meta2.1$is.PHE, stringsAsFactors = F)

# Prepare Major lineage dataset
TPA.rawseq.Lineage.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Lineage=TPA.meta2.1$TPA_Lineage, stringsAsFactors = F)

# Prepare sublineage lineage dataset
TPA.rawseq.subLineage.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage, stringsAsFactors = F)


# Prepare Year dataset (all samples)
TPA.rawseq.all.Years.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Year=TPA.meta2.1$Sample_Year, stringsAsFactors = F)


floor_5years  <- function(value){ return(value - value %% 5) }
TPA.meta2.1$Sample_5year.window <- paste0(floor_5years(as.numeric(TPA.meta2.1$Sample_Year)),"-",floor_5years(as.numeric(TPA.meta2.1$Sample_Year))+5)
Warning in floor_5years(as.numeric(TPA.meta2.1$Sample_Year)) :
  NAs introduced by coercion
Warning in floor_5years(as.numeric(TPA.meta2.1$Sample_Year)) :
  NAs introduced by coercion
# Some samples have uncertain dates (up to 20-30 years uncertainty), but for the purposes of these plotting categories we'll use the centrepoint year
TPA.meta2.1$Sample_5year.window <- sapply(1:nrow(TPA.meta2.1), function(x) ifelse(TPA.meta2.1$Sample_Year[x]=="-",NA, ifelse(is.na(TPA.meta2.1$Sample_5year.window[x]),NA, ifelse(TPA.meta2.1$Sample_Year[x]=="1950-1980","1965-1970",ifelse(TPA.meta2.1$Sample_Year[x]=="1960-1980","1965-1970" ,ifelse(TPA.meta2.1$Sample_Year[x]=="1980-1999","1985-1990",TPA.meta2.1$Sample_5year.window[x]))))))


TPA.meta2.1$Sample_year.1990.cuttoff <- ifelse(TPA.meta2.1$Sample_Year>1990,TPA.meta2.1$Sample_Year,"<1990")

TPA.meta2.1$Sample_year.1999.cuttoff <- ifelse(TPA.meta2.1$Sample_Year>1999,TPA.meta2.1$Sample_Year,"<1999")
TPA.rawseq.year.cuttoff.p <- data.frame(row.names=TPA.meta2.1$Sample_Name, Sample.Year=TPA.meta2.1$Sample_year.1999.cuttoff, stringsAsFactors = F)



# Bring in PHE metadata

PHE.metadata.linked <- readxl::read_excel(PHE.metadata.linked.file)


Do some cleanup and factoring of variables


PHE.metadata.linked$age_group <- factor(PHE.metadata.linked$age_group, levels=rev(c("16-24","25-34","35-44","45+","Unknown")))

PHE.metadata.linked$london <- factor(PHE.metadata.linked$london,levels=rev(c("Yes","No","Unknown")))
PHE.metadata.linked$ukborn <- factor(PHE.metadata.linked$ukborn,levels=rev(c("Yes","No","Unknown")))
PHE.metadata.linked$hivpos <- factor(PHE.metadata.linked$hivpos, levels=rev(c("Yes","No","Unknown")))

# need to update terminology of 'MSM' to 'GBMSM'
PHE.metadata.linked[PHE.metadata.linked$gender_orientation=="MSM","gender_orientation"] <- "GBMSM"
PHE.metadata.linked$gender_orientation <- factor(PHE.metadata.linked$gender_orientation, levels=rev(c("MSW","GBMSM","WSM","MUnknown","Unknown")))

PHE.metadata.linked$phe_centre <- factor(PHE.metadata.linked$phe_centre, levels=rev(c("East Midlands", "East of England", "London", "North East", "North West", "South East", "South West", "West Midlands", "Yorkshire and Humber", "UK (not England)", "Not Known")))

PHE.metadata.linked$TPA.pinecone.sublineage <-  factor(PHE.metadata.linked$TPA.pinecone.sublineage, levels=sublineages.cols.brew$sublineage)



### Extract information about duplicates

PHE.metadata.duplicates <- PHE.metadata.linked[!is.na(PHE.metadata.linked$dup_flag),]
PHE.metadata.duplicates <- PHE.metadata.duplicates[!is.na(PHE.metadata.duplicates$Sample_Name),]


PHE.patient.matches <- data.frame(
    stringsAsFactors = FALSE,
                                   dup_flag = c("1A","1B",
                                                "2A","2B","3A","3B","4A",
                                                "4B","5A","5B"),
                                dup_Patient = c("Patient 1",
                                                "Patient 1","Patient 2",
                                                "Patient 2","Patient 3","Patient 3",
                                                "Patient 4","Patient 4",
                                                "Patient 5","Patient 5"),
                         dup_Patient_Sample = c("sample 1",
                                                "sample 2","sample 1",
                                                "sample 2","sample 1","sample 2",
                                                "sample 1","sample 2","sample 1",
                                                "sample 2")
                       )
                       

PHE.metadata.duplicates <- left_join(PHE.metadata.duplicates, PHE.patient.matches, by="dup_flag")

PHE.metadata.duplicates

Duplicate Samples missing metadata are all ‘new duplicates’ and were excluded due to low mapping coverage (all checked).
Samples labelled ‘ZA’ and ‘XB’ had duplicates in the original dataset, but the reciprocal pairs were excluded due to quality isues.
Available pairs - Patient 3, Patient 4

PHE.metadata.duplicates.paired <- PHE.metadata.duplicates[PHE.metadata.duplicates$dup_Patient %in% c("Patient 3","Patient 4"),]
PHE.metadata.duplicates.paired[order(PHE.metadata.duplicates.paired$dup_Patient, PHE.metadata.duplicates.paired$year,PHE.metadata.duplicates.paired$month),c("Sample_Name","dup_Patient", "month.fix", "year")]


These will be revisited later in the analysis.
Patient 4 HIV-ve MSM (45+), UK born, PHE region D 2 samples, collected in the same month and year Both samples are sublineage 1, and identical (0 pwSNPs) Likely the same infection (depending on dates, treatment, etc), but can’t rule out reinfection with same strain.
Patient 3 HIV-ve MSM (35-44), not UK born, based in London (C) 2 samples, collected 9 months apart Both samples are sublineage 1, but have 7 pairwise SNPs between them (loads!) Reinfection – probably from a different transmission network

However, based on the sample dates, as well as the outcome of the downstream genetic analysis, we can see that Patient 3 has duplicate infection events (different dates, 10 months apart) and the genomes are distinct (7 SNPs apart), whereas Patient 4 samples were collected in the same month and year (i.e. are likely duplicates from the same infection) and has identical genomes.
For downstream analysis purposes, we will retain both samples for Patient 3 (discrete infections), but exclude one sample from Patient 4 (duplicate infection samples) - ‘PHE150126A’ has much better genome coverage, so exclude ‘PHE150125A’

### Further Exclusions
PHE130056A - duplicate of PHE130057B (already removed, so not relevant) - don’t exclude! PHE170402A - quality control sample PHE170378A - quality control sample


Exclude duplicate sequences

duplicate.exclusion.list <- c("PHE150125A","PHE170402A","PHE170378A")
PHE.metadata.linked <- PHE.metadata.linked[PHE.metadata.linked$Sample_Name %notin% duplicate.exclusion.list,]


Moving on…

Define some colour schemes

# define some colors for each region
PHE.region.cols.brew <- data.frame(UKHSA.region=c("North East", "North West", "Yorkshire and Humber", "East Midlands", "West Midlands", "East of England", "London", "South East","South West","UK (not England)", "Not Known"), stringsAsFactors=F)
PHE.region.cols.brew$region.col <- c("#A6CEE3","#1F78B4","#CAB2D6","#33A02C","#B2DF8A","#FF7F00","#E31A1C","#FB9A99","#D4BB02","grey75","grey25")

# HIV color scheme
PHE.hiv.cols <- data.frame(hivpos=rev(sort(unique(PHE.metadata.linked$hivpos))), stringsAsFactors=F)
PHE.hiv.cols$hiv.cols <- c("#1f78b4","#b2df8a","grey75")

# Orientation colour scheme
PHE.orientation.cols <- data.frame(orientation=rev(sort(unique(PHE.metadata.linked$gender_orientation))), stringsAsFactors=F)
PHE.orientation.cols$orientation <- factor(PHE.orientation.cols$orientation, levels=rev(sort(unique(PHE.metadata.linked$gender_orientation))), labels=c("MSW","GBMSM","WSM","MUnknown","Unknown"))
PHE.orientation.cols$orientation.cols <- c("#1f78b4","#b2df8a","#fb9a99","#a6cee3","grey75")

# UK born colour scheme
PHE.ukborn.cols <- data.frame(ukborn=rev(sort(unique(PHE.metadata.linked$ukborn))),ukborn.cols=c("#1f78b4","#b2df8a","grey75"),stringsAsFactors = F)

# London based colour scheme
PHE.london.cols <- data.frame(london=rev(sort(unique(PHE.metadata.linked$london))),london.cols=c("#1f78b4","#b2df8a","grey75"),stringsAsFactors = F)


# Age group colour scheme
PHE.Age.cols <- data.frame(age_group=rev(sort(unique(PHE.metadata.linked$age_group))),stringsAsFactors = T)
PHE.Age.cols$age_group.cols <- c(brewer.pal(n=4,"YlGnBu"),"grey75")

# Sample Date colour scheme
PHE.year.cols <- data.frame(year=(sort(unique(PHE.metadata.linked$year))),stringsAsFactors = T)
PHE.year.cols$year.cols <- brewer.pal(n=7,"YlOrRd")

# Sample Date (all global data, but with 1990 cuttoff)
TPA.year.cuttoff.cols <- data.frame(date.cuttoff=c("<1999",1999:2019), date.cuttoff.col=c("#F2F2F2",colorRampPalette(brewer.pal(7, "YlOrRd"))(length(1999:2019))))



##### ## First describe the sequenced population as a whole
Set order of PHE regions

PHE.metadata.linked$phe_centre <- factor(PHE.metadata.linked$phe_centre, levels=rev(PHE.region.cols.brew$UKHSA.region))


Generate some basic statistics about geographical PHE regions (anonymised)

PHE.count.all <- PHE.metadata.linked %>% 
  dplyr::summarise(count.per.region=n())

PHE.count.years <- PHE.metadata.linked %>% 
  dplyr::group_by(year) %>%
  dplyr::summarise(count.per.year=n()) %>%
  ungroup() %>%
  dplyr::mutate(perc.per.year=(count.per.year/sum(count.per.year))*100)

# Generate some stats about HIV status
PHE.HIV.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(hivpos) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::mutate(fraction=Count/total.region) %>%
  dplyr::arrange(desc(hivpos), .by_group=T) %>%
  dplyr::mutate(cum_fract = cumsum(fraction)) %>%
  dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(HIV.perc=(Count/sum(Count)*100))
  
# Generate some stats about gender orientation
PHE.orientation.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(gender_orientation) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>% 
  dplyr::mutate(orientation.perc=(Count/sum(Count)*100))

# Generate some stats about UK born (vague category that's unfortunately only marginally helpful)
PHE.UKborn.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(ukborn) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(ukborn), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>% 
  dplyr::mutate(UKborn.perc=(Count/sum(Count)*100))
  
# Generate some stats about London based
PHE.London.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(london) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(london), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(London.perc=(Count/sum(Count)*100))

# Generate some stats about Age group
PHE.Age.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(age_group) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(age_group), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(Age.perc=(Count/sum(Count)*100))

# Generate some stats about Lineage group
PHE.Lineage.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA_Lineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(TPA_Lineage), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(Lineage.perc=(Count/sum(Count)*100))

# Generate some stats about sublineage group
PHE.sublineage.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(Sublineage.perc=(Count/sum(Count)*100))


Make some plots

# Make hbar plot of sample counts by region
p.all.hbarplot <- ggplot(PHE.count.all, aes(x=count.per.region,y="")) +
  geom_barh(stat="identity", position="stack", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(values="grey30") + 
  geom_text(data=PHE.count.all, aes((count.per.region+12), "",label=count.per.region), size=theme.text.size.within, inherit.aes = F) +
  labs(y="All", x="Sample Count") +
  coord_cartesian(xlim=c(0,260)) +
  guides(fill=guide_legend(nrow=4)) 
#p.all.hbarplot

# make temporal bubbleplot of counts by region
p.all.year.bubbleplot <- ggplot(PHE.count.years, aes(as.numeric(year), y="All")) +
  geom_point(alpha=0.65, aes(size=count.per.year)) + 
  geom_line(alpha=0.25) +
  guides(colour='none') +
  scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) + 
  guides(size=guide_legend(nrow=2)) +
  theme_light() +
  scale_fill_manual(values="grey30") + 
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  labs(y="", x="Sample Year", size="Count") 
#p.all.year.bubbleplot

# Make proportional hbar plot of HIV status
p.all.hiv.hbarplot <- ggplot(PHE.HIV.counts, aes(Count,y="",fill=hivpos)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
  labs(y="All", x="HIV +ve") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.HIV.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F) +
  NULL
#p.all.hiv.hbarplot

p.all.orientation.hbarplot <- ggplot(PHE.orientation.counts, aes(Count,y="",fill=gender_orientation)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
  labs(y="All", x="Orientation") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.orientation.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.orientation.hbarplot

p.all.ukborn.hbarplot <- ggplot(PHE.UKborn.counts, aes(Count,y="",fill=ukborn)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(name="UK\nBorn",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
  labs(y="All", x="UK Born") +
  #guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.UKborn.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.ukborn.hbarplot

p.all.London.hbarplot <- ggplot(PHE.London.counts, aes(Count,y="",fill=london)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(name="London",values=PHE.london.cols$london.cols, breaks=PHE.london.cols$london) +
  labs(y="All", x="London") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.London.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.London.hbarplot

p.all.Age.hbarplot <- ggplot(PHE.Age.counts, aes(Count,y="",fill=age_group)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='none') +
  scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
  labs(y="All", x="Age Group") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.Age.counts, aes(cum_fract.mid, y="",label=Count), size=theme.text.size.within, inherit.aes = F)
#p.all.Age.hbarplot


Plot combined plot for ‘all samples’

PHE.all.combiplot.1 <- plot_grid(p.all.year.bubbleplot, p.all.hbarplot + y.theme.strip, p.all.orientation.hbarplot + y.theme.strip, p.all.hiv.hbarplot + y.theme.strip, p.all.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)

PHE.all.combiplot.1



Next just describe population distributions by PHE region

# generate some basic statistics about geographical PHE regions (anonymised)
PHE.geo.count <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre) %>%
  dplyr::summarise(count.per.region=n()) %>%
  dplyr::mutate(total.count=sum(count.per.region),fraction=count.per.region/total.count)

PHE.geo.count.years <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre,year) %>%
  dplyr::summarise(count.per.region.year=n())
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
PHE.geo.count.years.lineage <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre,year,TPA_Lineage) %>%
  dplyr::summarise(count.per.region.year=n()) %>%
  dplyr::mutate(total.count.year=sum(count.per.region.year)) %>%
  dplyr::ungroup() %>%
  tidyr::pivot_wider(names_from=TPA_Lineage, values_from = count.per.region.year)
`summarise()` has grouped output by 'phe_centre', 'year'. You can override using the `.groups` argument.
PHE.geo.count.years.lineage[is.na(PHE.geo.count.years.lineage)] <- 0
PHE.geo.count.years.lineage$year <- as.numeric(PHE.geo.count.years.lineage$year)

# Generate some stats about HIV status
PHE.geo.HIV.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre,hivpos) %>%
  dplyr::summarise(count.per.region.hiv=n()) %>%
  dplyr::mutate(total.region=sum(count.per.region.hiv)) %>%
  dplyr::mutate(fraction=count.per.region.hiv/total.region) %>%
  dplyr::arrange(desc(hivpos), .by_group=T) %>%
  dplyr::mutate(cum_fract = cumsum(fraction)) %>%
  dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Double Check HIV status data for non-PHE dataset - confirmed no HIV+ves from non-MSM. 
PHE.sourcelab.HIV.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(is.PHE, gender_orientation, hivpos) %>%
  dplyr::summarise(count.per.orientation.hiv=n()) #%>%
`summarise()` has grouped output by 'is.PHE', 'gender_orientation'. You can override using the `.groups` argument.
  #dplyr::filter(is.PHE!="PHE")

# Get total population stats for HIV
PHE.all.HIV.counts <-  PHE.metadata.linked %>% 
  dplyr::group_by(hivpos) %>%
  dplyr::summarise(count.hiv=n()) %>%
  dplyr::mutate(count.total=sum(count.hiv), fraction=count.hiv/count.total)

  
# Generate some stats about gender orientation
PHE.orientation.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(gender_orientation) %>%
  dplyr::summarise(orientation.count=n()) %>%
  dplyr::mutate(orientation.percent=(orientation.count/sum(orientation.count)*100))

PHE.geo.orientation.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre,gender_orientation) %>%
  dplyr::summarise(count.per.region.orientation=n()) %>%
  dplyr::mutate(total.region=sum(count.per.region.orientation)) %>%
  dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
  dplyr::mutate(fraction=count.per.region.orientation/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) %>% 
  dplyr::mutate(orientation.percent=(count.per.region.orientation/sum(count.per.region.orientation)*100))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about UK born
PHE.geo.UKborn <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre, ukborn) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(ukborn), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
  
# Generate some stats about London based
PHE.geo.London <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre, london) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(london), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about Age group
PHE.geo.Age <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre, age_group) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(age_group), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about Lineage group
PHE.geo.Lineage <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre, TPA_Lineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(TPA_Lineage), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.
# Generate some stats about sublineage group
PHE.geo.sublineage <- PHE.metadata.linked %>% 
  dplyr::group_by(phe_centre, TPA.pinecone.sublineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.region=sum(Count)) %>%
  dplyr::arrange(desc(TPA.pinecone.sublineage), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.region, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'phe_centre'. You can override using the `.groups` argument.


Make some plots

# Make hbar plot of sample counts by region
p.region.hbarplot <- ggplot(PHE.geo.count, aes(count.per.region,phe_centre, fill=phe_centre)) +
  geom_barh(stat="identity", position="stack", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
  geom_text(data=PHE.geo.count, aes((count.per.region+12), phe_centre,label=count.per.region), size=theme.text.size.within, inherit.aes = F) +
  labs(y="UKHSA Region", x="Sample Count") +
  #coord_cartesian(xlim=c(0,130)) +
  coord_cartesian(xlim=c(0,260)) +
  guides(fill=guide_legend(ncol=2)) 
#p.region.hbarplot

# make temporal bubbleplot of counts by region
p.region.year.bubbleplot <- ggplot(PHE.geo.count.years, aes(as.numeric(year), phe_centre, colour=phe_centre)) +
  geom_point(alpha=0.65, aes(size=count.per.region.year)) + 
  geom_line(alpha=0.25) +
  guides(colour='none') +
  scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) + 
  guides(size=guide_legend(nrow=2, direction = 'horizontal', byrow=T)) +
  theme_light() +
  scale_color_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  labs(y="UKHSA Region", x="Sample Year", size="Count") 
#p.region.year.bubbleplot

# Or a barplot of lineage by year & PHE region?
p.region.year.bubbleplot.barplot.facet.lineage <- PHE.geo.count.years.lineage %>% tidyr::pivot_longer(c(SS14, Nichols), names_to="TPA_Lineage", values_to="Count") %>%
  ggplot(aes(year, Count, fill=TPA_Lineage)) + 
  geom_bar(stat='identity', width=0.6) + 
  facet_grid(phe_centre~., scales='free') +
  guides(size=guide_legend(nrow=2)) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) +
  theme(strip.background = element_rect(color='white', fill='white',linetype="solid"), strip.text.y = element_text(color = "grey25", size=7, angle=0)) 
#p.region.year.bubbleplot.barplot.facet.lineage

# Make proportional hbar plot of HIV status
p.region.hiv.hbarplot <- ggplot(PHE.geo.HIV.counts, aes(count.per.region.hiv,phe_centre,fill=hivpos)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
  labs(y="UKHSA Region", x="HIV +ve") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.geo.HIV.counts, aes(cum_fract.mid, phe_centre,label=count.per.region.hiv), size=theme.text.size.within, inherit.aes = F) +
  NULL
#p.region.hiv.hbarplot

p.region.orientation.hbarplot <- ggplot(PHE.geo.orientation.counts, aes(count.per.region.orientation,phe_centre,fill=gender_orientation)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
  labs(y="UKHSA Region", x="Orientation") +
  guides(fill=guide_legend(ncol=1)) +
  geom_text(data=PHE.geo.orientation.counts, aes(cum_fract.mid, phe_centre,label=count.per.region.orientation), size=theme.text.size.within, inherit.aes = F)
#p.region.orientation.hbarplot

p.region.ukborn.hbarplot <- ggplot(PHE.geo.UKborn, aes(Count,phe_centre,fill=ukborn)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="UK Born",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
  labs(y="UKHSA Region", x="UK Born") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.geo.UKborn, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.ukborn.hbarplot

p.region.London.hbarplot <- ggplot(PHE.geo.London, aes(Count,phe_centre,fill=london)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="London",values=PHE.london.cols$london.cols, breaks=PHE.london.cols$london) +
  labs(y="UKHSA Region", x="London") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.geo.London, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.London.hbarplot

p.region.Age.hbarplot <- ggplot(PHE.geo.Age, aes(Count,phe_centre,fill=age_group)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
  labs(y="UKHSA Region", x="Age Group") +
  guides(fill=guide_legend(ncol=1)) +
  geom_text(data=PHE.geo.Age, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.Age.hbarplot


Combined plot

PHE.region.combiplot.1 <- plot_grid(p.region.year.bubbleplot, p.region.hbarplot + y.theme.strip, p.region.orientation.hbarplot + y.theme.strip, p.region.hiv.hbarplot + y.theme.strip, p.region.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)

PHE.region.combiplot.1


Regions as a complex multipanel plot



# legends
PHE.region.combiplot.1.legends <- plot_grid(get_legend(p.region.year.bubbleplot), get_legend(p.region.hbarplot + y.theme.strip), get_legend(p.region.orientation.hbarplot + y.theme.strip), get_legend(p.region.hiv.hbarplot + y.theme.strip), get_legend(p.region.Age.hbarplot + y.theme.strip), nrow=1, align="h", rel_widths=c(6,4,4,4,4), scale=0.95)


# Arrange plots vertically
p.year.bubbleplot.combi <- plot_grid(p.all.year.bubbleplot + x.theme.strip, p.region.year.bubbleplot + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.region.hbar.counts.combi <- plot_grid(p.all.hbarplot + x.theme.strip + y.theme.strip, p.region.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.region.hbar.orientation.combi <- plot_grid(p.all.orientation.hbarplot + x.theme.strip + y.theme.strip, p.region.orientation.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.region.hbar.hiv.combi <- plot_grid(p.all.hiv.hbarplot + x.theme.strip + y.theme.strip, p.region.hiv.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.region.hbar.Age.combi <- plot_grid(p.all.Age.hbarplot + x.theme.strip + y.theme.strip, p.region.Age.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

# Combine the plots
p.region.hbar.combi.plus.all <- plot_grid(p.year.bubbleplot.combi, p.region.hbar.counts.combi, p.region.hbar.orientation.combi, p.region.hbar.hiv.combi, p.region.hbar.Age.combi, nrow=1, rel_widths=c(6,4,4,4,4), labels = c("A","B","C","D","E"), label_size=panel.lab.size, vjust=0.25)
# and add the legends on top
p.region.hbar.combi.plus.all.with.legends <- plot_grid(p.region.hbar.combi.plus.all, PHE.region.combiplot.1.legends, ncol=1, rel_heights=c(6,1), scale = 0.95)



p.region.hbar.combi.plus.all.with.legends

#ggsave(paste0(Figure_output_directory, "SupFig2_TPA-PHE_Sample-metadistros-by-phe_region+all-combi.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=240, height=135, device='pdf', dpi=1200)



Now lets look at some genetic data
### Make ML tree with sublineage tippoints

TPA.MLtree.ggtree.tippoint <- TPA.MLtree.ggtree %<+% data.frame(Sample_Name=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage,stringsAsFactors = F) + 
  geom_tippoint(aes(color=Sublineage), size=0.5, alpha=0.5, show.legend = FALSE) + 
  scale_color_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)


Add metadata

# Continent
p.TPA.MLtree.PHE <- gheatmap(TPA.MLtree.ggtree.tippoint,
               TPA.rawseq.continents.p, color=NULL,width=0.075,offset=0.00000025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) + 
  scale_fill_manual(name="Continent",values=continental.cols.brew2$continent.col, breaks=continental.cols.brew2$Continent, guide = guide_legend(order = 1,ncol=2)) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# is UK
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE,
               TPA.rawseq.UK.p, color=NULL,width=0.075,offset=0.00001025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) + 
  scale_fill_manual(name="England/Other", values=c("black","grey95"), breaks=c("England","Other"), guide = guide_legend(order = 2,ncol=2)) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Lineage
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE,TPA.rawseq.Lineage.p, color=NULL,width=0.075,offset=0.00002025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) + 
  scale_fill_manual(name="Lineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage, guide = guide_legend(order = 3, ncol=2)) + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
   new_scale_fill() +
  NULL
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# sublineage
p.TPA.MLtree.PHE <- gheatmap(p.TPA.MLtree.PHE, data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage,stringsAsFactors = F), color=NULL,width=0.075,offset=0.00003025, colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) + 
  scale_fill_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, guide = guide_legend(order = 4, ncol=3)) + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
   new_scale_fill() +
  NULL
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.


plot

p.TPA.MLtree.PHE


#ggsave(paste0(Figure_output_directory, "SupFig3_TPA-PHE_Global_Phylo+UK-highlights.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=160, device='pdf', dpi=1200)



### Geographic distributions of Lineages and Sublineages What about sublineages?

p.region.Lineage.hbarplot <- ggplot(PHE.geo.Lineage, aes(Count,phe_centre,fill=TPA_Lineage)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) +
  labs(y="UKHSA Region", x="TPA Lineage") +
  guides(fill=guide_legend(nrow=3)) +
  #geom_text(data=PHE.geo.Lineage, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F) +
  NULL

p.region.sublineage.hbarplot <- ggplot(PHE.geo.sublineage, aes(Count,phe_centre,fill=TPA.pinecone.sublineage)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
  labs(y="UKHSA Region", x="TPA Sublineage") +
  guides(fill=guide_legend(nrow=4)) +
  #geom_text(data=PHE.geo.sublineage, aes(cum_fract.mid, phe_centre,label=Count), size=theme.text.size.within, inherit.aes = F) +
  NULL


Combi plot (geography lineages)

PHE.region.combiplot.2.lineages <- plot_grid(p.region.year.bubbleplot +legend.strip, p.region.hbarplot + y.theme.strip + legend.strip + coord_cartesian(xlim=c(0,150)), p.region.Lineage.hbarplot + y.theme.strip +legend.strip, p.region.sublineage.hbarplot + y.theme.strip +legend.strip, nrow=1, align="h", rel_widths=c(6,3,4,4), scale=0.99, labels=c("C","D","E","F"), label_size=panel.lab.size)
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# separate out the plot for the legends
p.region.year.bubbleplot.legend <- get_legend(p.region.year.bubbleplot)
p.region.hbarplot.legend <- get_legend(p.region.hbarplot + y.theme.strip)
p.region.Lineage.hbarplot.legend <- get_legend(p.region.Lineage.hbarplot + y.theme.strip)
p.region.sublineage.hbarplot.legend <- get_legend(p.region.sublineage.hbarplot + y.theme.strip)

PHE.region.combiplot.2.lineages.legend <- plot_grid(p.region.year.bubbleplot.legend, p.region.hbarplot.legend, p.region.Lineage.hbarplot.legend, p.region.sublineage.hbarplot.legend, nrow=1, align="h", rel_widths=c(6,3,4,4))

PHE.region.combiplot.2.lineages <- plot_grid(PHE.region.combiplot.2.lineages, PHE.region.combiplot.2.lineages.legend, rel_heights = c(4,1), ncol=1)

PHE.region.combiplot.2.lineages


OK, let’s now add a map of these geographical distributions


Let’s used ONS published shape files - there is one available that shows Public Health England region boundaries.


# Generate approximate regional GPS coords
PHE.region.GPS <- data.frame(
  stringsAsFactors = FALSE,
          phe_centre = c("East Midlands",
                         "East of England","London","North East","North West",
                         "South East","South West","West Midlands",
                         "Yorkshire and Humber","UK (not England)","Not Known"),
            Longitude = c(-0.7,0.5,-0.2,-1.9,-2.4,
                         0.05,-2.9,-2,-0.8,0.1,0.63),
           Latitude = c(52.9,52.4,51.5,55,53.7,
                         51.1,51,52.6,53.8,54.7,54.1)
  )  
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="SS14",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS)[4] <- "SS14"
PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="Nichols",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS)[5] <- "Nichols"
PHE.region.GPS[is.na(PHE.region.GPS)] <- 0

PHE.region.GPS <- left_join(PHE.region.GPS, PHE.geo.Lineage[PHE.geo.Lineage$TPA_Lineage=="SS14",c("phe_centre","total.region")], by="phe_centre")
colnames(PHE.region.GPS)[6] <- "Region_Count"

PHE.region.GPS$radius <- 0.5*(1-1/sqrt(PHE.region.GPS$Region_Count))


###############################
# Import datafile from https://geoportal.statistics.gov.uk/datasets/public-health-england-centres-december-2016-full-clipped-boundaries-in-england/explore?location=52.950000%2C-2.000000%2C6.88

UK.shapefile <- readOGR(dsn=UK.publichealth.shapefile.data)
Warning in OGRSpatialRef(dsn, layer, morphFromESRI = morphFromESRI, dumpSRS = dumpSRS,  :
  Discarded datum Ordnance_Survey_of_Great_Britain_1936 in Proj4 definition: +proj=tmerc +lat_0=49 +lon_0=-2 +k=0.9996012717 +x_0=400000 +y_0=-100000 +ellps=airy +units=m +no_defs
OGR data source with driver: ESRI Shapefile 
Source: "/Users/mb29/Papers/Treponema_UK-PHE-gen-epi_2021/Rnotebook/Rnotebook_09-2022/inputdata/Public_Health_England_Centres_(December_2016)_Boundaries", layer: "Public_Health_England_Centres_(December_2016)_Boundaries"
with 9 features
It has 9 fields
#Reshape for ggplot2 using the Broom package
UK.mapdata <- tidy(UK.shapefile, region="phec16nm")

#UK.gg <- ggplot() + geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color = "#FFFFFF", size = 0.25)
UK.gg <- ggplot() + geom_polygon(data = UK.mapdata, aes(x = long, y = lat, group = group), color="grey25", fill="grey90", size = 0.075)

UK.gg <- UK.gg + coord_fixed(1) + theme_nothing()
#UK.gg

# Convert UK regions to be compatible with map
# First find centre point for each region
UK.mapdata.regions.meancoords <- UK.mapdata %>% dplyr::group_by(id) %>%
  dplyr::summarise(mean.lat=mean(lat), mean.long=median(long)) %>%
  dplyr::ungroup()
colnames(UK.mapdata.regions.meancoords)[1] <- "phe_centre"

PHE.region.GPS.ukmap <- dplyr::left_join(PHE.region.GPS, UK.mapdata.regions.meancoords, by="phe_centre")

# Add artificial location for 'not known'
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="Not Known","mean.lat"] <- 600000
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="Not Known","mean.long"] <- 550000

# Shift "South East" slightly to reduce the overlap with London
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="South East","mean.long"] <- 475000
# Shift "East of England East" slightly to reduce the overlap with London 
PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre=="East of England","mean.lat"] <- 275000

# Not going to try plotting the 2 samples from elsewhere in the UK, so remove that row
PHE.region.GPS.ukmap <- PHE.region.GPS.ukmap[PHE.region.GPS.ukmap$phe_centre != "UK (not England)",]

# Create radius variable for plotting pie sizes (use log10(n)*20,000)
PHE.region.GPS.ukmap$radius.UK <- log10(PHE.region.GPS.ukmap$Region_Count)*20000

#PHE.geo.count.years.lineage

UK.gg.scatterpie <- UK.gg + geom_scatterpie(data=PHE.region.GPS.ukmap, aes(mean.long, mean.lat, group=phe_centre, r=radius.UK), alpha=0.85, color=NA, cols=c("Nichols","SS14")) + 
  scale_fill_manual(name="TPA\nLineage",values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage) + theme(legend.position="top")

UK.gg.scatterpie <- UK.gg.scatterpie + geom_scatterpie_legend(PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),"radius.UK"], labeller=function(x) round((10^(x/20000)),0), n=3, x=150000, y=500000)

UK.gg.scatterpie <- UK.gg.scatterpie + theme_nothing()

#? Add labels
UK.gg.scatterpie.labs <- UK.gg.scatterpie + geom_label_repel(data=PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),], aes(mean.long, mean.lat, label=phe_centre), size=theme.text.size.within, nudge_x = 50000, nudge_y = -25000, segment.size  = 0.1) + theme(legend.key.size = unit(0.55,"line"), legend.position="bottom") + 
  theme.text.size +
  theme_nothing()

UK.gg.scatterpie.labs



Now do an equivalent plot for sublineages

PHE.region.GPS.ukmap.sublin <- PHE.region.GPS.ukmap


PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="1",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[11] <- "1"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="2",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[12] <- "2"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="3",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[13] <- "3"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="6",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[14] <- "6"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="8",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[15] <- "8"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="14",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[16] <- "14"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="15",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[17] <- "15"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="16",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[18] <- "16"
PHE.region.GPS.ukmap.sublin <- left_join(PHE.region.GPS.ukmap.sublin, PHE.geo.sublineage[PHE.geo.sublineage$TPA.pinecone.sublineage=="Singleton",c("phe_centre","Count")], by="phe_centre")
colnames(PHE.region.GPS.ukmap.sublin)[19] <- "Singleton"
PHE.region.GPS.ukmap.sublin[is.na(PHE.region.GPS.ukmap.sublin)] <- 0

# Most samples are either sublineage 1 or 14. Let's create a count of samples that are neither.
PHE.region.GPS.ukmap.sublin$`Other Sublineages` <- sapply(1:nrow(PHE.region.GPS.ukmap.sublin), function (x) PHE.region.GPS.ukmap.sublin$Region_Count[x]-sum(PHE.region.GPS.ukmap.sublin$`1`[x], PHE.region.GPS.ukmap.sublin$`14`[x])) 



UK.gg.scatterpie.sublineage <- UK.gg + geom_scatterpie(data=PHE.region.GPS.ukmap.sublin[PHE.region.GPS.ukmap.sublin$mean.long!=0,], aes(mean.long, mean.lat, group=phe_centre, r=radius.UK), alpha=0.85, color=NA, cols=c("1","14","Other Sublineages")) + 
  scale_fill_manual(name="TPA\nSublineage",values=c("#FC9272","#BCBDDC", "grey50"), breaks=c("1","14","Other Sublineages"))

# add legend
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + geom_scatterpie_legend(PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),"radius.UK"], labeller=function(x) round((10^(x/20000)),0), n=3, x=150000, y=500000)

#UK.gg.scatterpie <- UK.gg.scatterpie + x.theme.strip + y.theme.strip
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + theme_nothing()

#? Add labels
UK.gg.scatterpie.sublineage <- UK.gg.scatterpie.sublineage + geom_label_repel(data=PHE.region.GPS.ukmap[!is.na(PHE.region.GPS.ukmap$mean.lat),], aes(mean.long, mean.lat, label=phe_centre), size=theme.text.size.within, nudge_x = 50000, nudge_y = -25000, segment.size  = 0.1) +
  theme(legend.key.size = unit(0.55,"line"), legend.position="bottom") + 
  theme.text.size +
  theme_nothing()


UK.gg.scatterpie.sublineage


Combined map plot

UK.gg.scatterpie.combi <- plot_grid(UK.gg.scatterpie.labs, UK.gg.scatterpie.sublineage, ncol=2, labels = c("A","B"), label_size=panel.lab.size)

UK.gg.scatterpie.combi



Plot in combination with barplots

plot_grid(UK.gg.scatterpie.combi, PHE.region.combiplot.2.lineages, nrow=2, rel_heights=c(4,5))


#ggsave(paste0(Figure_output_directory,"Fig2_TPA-PHE_Map-Lineage+Barplots.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=190, height=185, device='pdf', dpi=1200)



### Analysis by sublineage
Now lets start exploring how samples are distributed by sublineage

PHE.metadata.linked <- PHE.metadata.linked
PHE.metadata.linked$TPA.pinecone.sublineage <- factor(PHE.metadata.linked$TPA.pinecone.sublineage, levels=rev(as.character(sort(unique(PHE.metadata.linked$TPA.pinecone.sublineage)))))

PHE.Lineage.count <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA_Lineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total=sum(Count), perc=(Count/total)*100)

PHE.sublin.count <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total=sum(Count), perc=(Count/total)*100)

PHE.geo.sublin.years <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage,year) %>%
  dplyr::summarise(Count=n())
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
## Generate some stats about sublineage groups

# Generate some stats about gender orientation
PHE.sublineage.orientation.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage,gender_orientation) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(gender_orientation), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about UK born
PHE.sublineage.UKborn <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage, ukborn) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  #dplyr::arrange(desc(ukborn), .by_group=T) %>%
  dplyr::arrange(desc(ukborn), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
  
# Generate some stats about London based
PHE.sublineage.London <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage, london) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(london), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about Age group
PHE.sublineage.Age <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage, age_group) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(age_group), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats about HIV group
PHE.sublineage.HIV <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage, hivpos) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(hivpos), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Generate some stats by PHE Region
PHE.sublineage.PHEcentre <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage, phe_centre) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(phe_centre), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.


Plot by sublineage

p.sublineage.year.bubbleplot <- ggplot(PHE.geo.sublin.years, aes(as.numeric(year), TPA.pinecone.sublineage, colour=TPA.pinecone.sublineage)) +
  geom_point(alpha=0.65, aes(size=Count)) + 
  geom_line(alpha=0.25) +
  guides(colour='none') +
  scale_size_area(max_size = 7,breaks=c(1,5,10,25,50)) + 
  guides(size=guide_legend(nrow=2, direction = 'horizontal', byrow=T)) +
  theme_light() +
  scale_color_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  labs(y="TPA Sublineage", x="Sample Year", size="Count") 
#p.sublineage.year.bubbleplot

p.sublineage.hbarplot <- ggplot(PHE.sublin.count, aes(Count,TPA.pinecone.sublineage,fill=TPA.pinecone.sublineage)) +
  geom_barh(stat="identity", position="stack", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="TPA\nSublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) +
  labs(y="TPA Sublineage", x="Sample Count") +
  geom_text(data=PHE.sublin.count, aes((Count+12), TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F) +
  #coord_cartesian(xlim=c(0,200)) +
  coord_cartesian(xlim=c(0,260)) +
  guides(fill=guide_legend(ncol=2))
#p.sublineage.hbarplot 

p.sublineage.orientation.hbarplot <- ggplot(PHE.sublineage.orientation.counts, aes(y=TPA.pinecone.sublineage,x=Count,fill=gender_orientation)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="Orientation",values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) +
  labs(y="TPA Sublineage", x="Orientation") +
  guides(fill=guide_legend(ncol=1)) +
  geom_text(data=PHE.sublineage.orientation.counts, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.region.orientation.hbarplot

p.sublineage.hiv.hbarplot <- ggplot(PHE.sublineage.HIV, aes(y=TPA.pinecone.sublineage, x=Count,fill=hivpos)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="HIV +ve",values=PHE.hiv.cols$hiv.cols, breaks=PHE.hiv.cols$hivpos) +
  labs(y="TPA Sublineage", x="HIV +ve") +
  guides(fill=guide_legend(ncol=1)) + 
  geom_text(data=PHE.sublineage.HIV, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.hiv.hbarplot

p.sublineage.ukborn.hbarplot <- ggplot(PHE.sublineage.UKborn, aes(y=TPA.pinecone.sublineage,x=Count,fill=ukborn)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="UK\nborn",values=PHE.ukborn.cols$ukborn.cols, breaks=PHE.ukborn.cols$ukborn) +
  labs(y="TPA Sublineage", x="UK born") +
  guides(fill=guide_legend(nrow=3)) +
  geom_text(data=PHE.sublineage.UKborn, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.ukborn.hbarplot

p.sublineage.Age.hbarplot <- ggplot(PHE.sublineage.Age, aes(y=TPA.pinecone.sublineage, x=Count ,fill=age_group)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="Age\nGroup",values=PHE.Age.cols$age_group.cols, breaks=PHE.Age.cols$age_group) +
  labs(y="TPA Sublineage", x="Age Group") +
  guides(fill=guide_legend(ncol=1)) +
  geom_text(data=PHE.sublineage.Age, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)
#p.sublineage.Age.hbarplot


p.sublineage.PHEregion.hbarplot <- ggplot(PHE.sublineage.PHEcentre, aes(y=TPA.pinecone.sublineage, x=Count, fill=phe_centre)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  scale_fill_manual(name="UKHSA\nRegion",values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$PHE.region) +
  labs(y="TPA Sublineage", x="UKHSA Region") +
  guides(fill=guide_legend(nrow=4)) +
  geom_text(data=PHE.sublineage.PHEcentre, aes(cum_fract.mid, TPA.pinecone.sublineage,label=Count), size=theme.text.size.within, inherit.aes = F)


Look at how sublineages are distributed by region (sublineage-centric)

p.sublineage.PHEregion.hbarplot


Combine patient metadata into a plot

#PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.PHEregion.hbarplot + y.theme.strip, p.sublineage.ukborn.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(3,2,2,2,2,2,2), scale=0.9)

#PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, p.sublineage.PHEregion.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(3,2,2,2,2,4), scale=0.9)

PHE.sublineages.combiplot.1 <- plot_grid(p.sublineage.year.bubbleplot, p.sublineage.hbarplot + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)

PHE.sublineages.combiplot.1 


Lets add the ‘all’ row again to the ‘by sublineage’ plot

# legends
PHE.sublineage.combiplot.1.legends <- plot_grid(get_legend(p.sublineage.year.bubbleplot), get_legend(p.sublineage.hbarplot + y.theme.strip), get_legend(p.sublineage.orientation.hbarplot + y.theme.strip), get_legend(p.sublineage.hiv.hbarplot + y.theme.strip), get_legend(p.sublineage.Age.hbarplot + y.theme.strip), nrow=1, align="h", rel_widths=c(6,4,4,4,4), scale=0.95)

# regions
#PHE.sublineage.combiplot.1.nolegend <- plot_grid(p.sublineage.year.bubbleplot + legend.strip, p.sublineage.hbarplot + y.theme.strip + legend.strip, p.sublineage.orientation.hbarplot + y.theme.strip + legend.strip, p.sublineage.hiv.hbarplot + y.theme.strip + legend.strip, p.sublineage.Age.hbarplot + y.theme.strip + legend.strip, nrow=1, align="h", rel_widths=c(4,2,2,2,2), scale=0.9)

# Or do it vertically
p.sublineage.year.bubbleplot.combi <- plot_grid(p.all.year.bubbleplot + x.theme.strip, p.sublineage.year.bubbleplot + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.sublineage.hbar.counts.combi <- plot_grid(p.all.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.sublineage.hbar.orientation.combi <- plot_grid(p.all.orientation.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.orientation.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.sublineage.hbar.hiv.combi <- plot_grid(p.all.hiv.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.hiv.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

p.sublineage.hbar.Age.combi <- plot_grid(p.all.Age.hbarplot + x.theme.strip + y.theme.strip, p.sublineage.Age.hbarplot + y.theme.strip + legend.strip, ncol=1, align="v",axis="lr", rel_heights=c(1,7))

# Combine the plots
p.sublineage.hbar.combi.plus.all <- plot_grid(p.sublineage.year.bubbleplot.combi, p.sublineage.hbar.counts.combi, p.sublineage.hbar.orientation.combi, p.sublineage.hbar.hiv.combi, p.sublineage.hbar.Age.combi, nrow=1, rel_widths=c(7,3,4,4,4), labels=c("A", "B", "C", "D", "E"),label_size=panel.lab.size, vjust=1, scale=0.99)

# and add the legends on top
#p.sublineage.hbar.combi.plus.all.with.legends <- plot_grid(PHE.sublineage.combiplot.1.legends, p.sublineage.hbar.combi.plus.all, ncol=1, rel_heights=c(1,9))

# legends below
p.sublineage.hbar.combi.plus.all.with.legends <- plot_grid(p.sublineage.hbar.combi.plus.all, PHE.sublineage.combiplot.1.legends, ncol=1, rel_heights=c(8,1))


p.sublineage.hbar.combi.plus.all.with.legends


  These patterns look fairly similar between sublineages, and (apart from 1 & 14) the groups are very small. However, sublineage 14 does appear to have a higher proportion of MSM compared to sublineage 1 and others. Let’s test that formally using 2x2 fisher’s tests

PHE.MSM.counts.all <- PHE.metadata.linked %>% 
  dplyr::group_by(is.MSM, .drop=F) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange((is.MSM), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))

PHE.sublineage.MSM.counts <- PHE.metadata.linked %>% 
  dplyr::group_by(TPA.pinecone.sublineage,is.MSM, .drop=F) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange((is.MSM), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2)) #%>%
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
  #dplyr::filter(!is.na(is.MSM))


PHE.sublineage.MSM.counts.wider <- PHE.sublineage.MSM.counts %>% dplyr::select(TPA.pinecone.sublineage, is.MSM, Count) %>%
  tidyr::pivot_wider(names_from = is.MSM, values_from=Count) %>%
  dplyr::mutate(MSM=replace_na(MSM, 0), Other=replace_na(Other, 0), Total=sum(MSM,Other)) %>%
  #dplyr::select(-`NA`) %>%
  dplyr::filter(Total!=0)
  

PHE.sublineage.MSM.pval <- data.frame(TPA.pinecone.sublineage=PHE.sublineage.MSM.counts.wider$TPA.pinecone.sublineage, p.fisher=sapply(1:nrow(PHE.sublineage.MSM.counts.wider), function (x) fisher.test(matrix(as.numeric(c(PHE.sublineage.MSM.counts.wider[x,"MSM"],
                                PHE.sublineage.MSM.counts.wider[x,"Other"],
                                PHE.MSM.counts.all[PHE.MSM.counts.all$is.MSM=="MSM","Count"], PHE.MSM.counts.all[PHE.MSM.counts.all$is.MSM=="Other","Count"])),nrow=2))[[1]]), stringsAsFactors=F)

PHE.sublineage.MSM.counts.wider <- dplyr::left_join(PHE.sublineage.MSM.counts.wider, PHE.sublineage.MSM.pval, by="TPA.pinecone.sublineage")

PHE.sublineage.MSM.counts.wider



### Visualisation of UK genomic relationships
Ok, let’s make a tree for displaying these relationships using the UK dataset only
From some experimentation, a ‘GrapeTree’ minimum spanning network works well for visualising the clonality of these populations. We can use a SNP-scaled phylogeny as direct input to GrapeTree, and this will allow branches to be scaled appropriately. However, although annotation is allowed within the GrapeTree software, colours must be manually edited. Final GrapeTree plots can then be imported back into R for combining with other plots.

Alternative visualisations - grapetree?
Take the 526-global phylogeny (snp-scaled version from pyjar), and prune to only include the UK strains from this study - this ensures the topology is consistent accross studies.


TPA.pyjar.tree.subset.uk <- ape::keep.tip(TPA.pyjar.tree, as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$Geo_Country=="UK","Sample_Name"])))

ggtree(TPA.pyjar.tree.subset.uk)

#write.tree(TPA.pyjar.tree.subset.uk, paste0(Data_input_directory,"TPA.UK-only.pyjar.2022-02-03.tre"))

# Write out a metadata sheet for the relevant information
PHE.metadata.linked.grapetree <- PHE.metadata.linked[,c("Sample_Name", "year","gender_orientation","phe_centre","hivpos","ukborn","TPA_Lineage","TPA.pinecone.sublineage")]
colnames(PHE.metadata.linked.grapetree)[1] <- "ID"

#write.table(PHE.metadata.linked.grapetree, paste0(Data_input_directory,"TPA.UK-only.grapetree.meta.2022-02-03.tsv"), sep = "\t", quote=F, row.names = F)


Tree independently visualised and annotated using GrapeTree.
Now import and integrate GrapeTree plot with metadata plots.

# Combine the plots
p.sublineage.hbar.combi.plus.all.B2F <- plot_grid(p.sublineage.year.bubbleplot.combi, p.sublineage.hbar.counts.combi, p.sublineage.hbar.orientation.combi, p.sublineage.hbar.hiv.combi, p.sublineage.hbar.Age.combi, nrow=1, rel_widths=c(7,4,4,4,4), labels=c("B", "C", "D", "E", "F"),label_size=panel.lab.size, vjust=1, scale=0.97)

# legends below
p.sublineage.hbar.combi.plus.all.with.legends.B2F <- plot_grid(p.sublineage.hbar.combi.plus.all.B2F, PHE.sublineage.combiplot.1.legends, ncol=1, rel_heights=c(7,1))

#p.sublineage.hbar.combi.plus.all.with.legends.B2F


# Now bring in externally plotted Grapetree
p.TPA.UK.Grapetree.sublineages <- ggdraw() + draw_image(TPA.UK.Grapetree.sublineages.file)
p.TPA.UK.Grapetree.sublineages


p.sublineage.hbar.combi.plus.all.with.legends.B2F.with.grapetree <- plot_grid(p.TPA.UK.Grapetree.sublineages, p.sublineage.hbar.combi.plus.all.with.legends.B2F, ncol=1, labels=c("A",""), label_size=panel.lab.size, rel_heights=c(3,5)) 


p.sublineage.hbar.combi.plus.all.with.legends.B2F.with.grapetree

#ggsave(paste0(Figure_output_directory, "Fig1_TPA-PHE_Sample-distros-sublineage.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=190, height=185, device='pdf', dpi=1200)


Manage other GrapeTree plots (for consistency)

TPA-UK-2022-02-16.-MSTree_3-way-figure.Inscaped-2

# Bring in 3-way graphetree plot (3 different metadata variables using the same input tree)
TPA.UK.Grapetree.3way <- ggdraw() + draw_image(TPA.UK.Grapetree.3way.file)
TPA.UK.Grapetree.3way


#ggsave(paste0(Figure_output_directory, "SupFig4_TPA-PHE_Grapetree-3ways.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=145, height=180, device='pdf', dpi=1200)


And also do the HIV status plot


TPA.UK.Grapetree.HIV <- ggdraw() + draw_image(TPA.UK.Grapetree.HIV.file)
TPA.UK.Grapetree.HIV


#ggsave(paste0(Figure_output_directory, "SupFig5_TPA-PHE_Grapetree-HIV.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=110, device='pdf', dpi=1200)



### Phylogenetic context analyses
Ok, now lets look at some trees
First, let’s formalise BEAST tree plotting as three separate functions to enable other trees to be plotted the same way

full.beast2.tree <- read.beast(full.beast2.tree.file)
full.beast2.tree@phylo$tip.label <- gsub("\\|.+$","",full.beast2.tree@phylo$tip.label, perl=T)

################################################################################################
# function to extract a tree based on sublineage
Extract_sublineage_tree_for_plot <- function(my.beast.tree, my.metadata, my.phe.meta, my.sublineage){
  # get all tips to include from metadata, then calculate MRCA from tree
  sublineage.test.mrca <- getMRCA(my.beast.tree@phylo, as.character(unlist(my.metadata[my.metadata$TPA.pinecone.sublineage==my.sublineage,"Sample_Name"])))
  ######
  TPA.beast.subtree.test <- tree_subset(my.beast.tree, node=sublineage.test.mrca, levels_back=0)
  return(TPA.beast.subtree.test)
}
#Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 1)
################################################################################################


################################################################################################
# Function to prepare a beast tree with timescale indicators, posterior support and 95% HPD bars
plot_beast_subtree_with_HPD <- function(my.beast.tree, my.metadata, my.phe.meta, mrsd.fulltree){
  # get MRCD for tree
  mrsd.Beast.tree.test.s <- max(as.numeric(unlist(my.metadata[my.metadata$Sample_Name %in% my.beast.tree@phylo$tip.label,"Sample_Year"])))
  mrsd.Beast.tree.test <- lubridate::ymd(paste0(mrsd.Beast.tree.test.s,"-06-01")) 
  mrsd.Beast.tree.fulltree <- lubridate::ymd(mrsd.fulltree) 
  #mrsd.Beast.tree.test
  # plot basic tree
  options(ignore.negative.edge=TRUE)
  p.TPA.beast.subtree.test <- ggtree(my.beast.tree, mrsd=mrsd.Beast.tree.test, ladderize = T, size=0.4) + scale_x_continuous(breaks=seq(1960,2020,10), minor_breaks=seq(2000, 2020, 1)) +
    theme_tree2() +
    # Add date lines for easy interpretation  
    theme(panel.grid.major   = element_line(color="grey50", size=.2),
          panel.grid.minor   = element_line(color="grey85", size=.2),
          panel.grid.major.y = element_blank(),
          panel.grid.minor.y = element_blank())
  # Add posterior support as node points
  p.TPA.beast.subtree.test <- p.TPA.beast.subtree.test + geom_point2(aes(subset=(!isTip & as.numeric(posterior)>0.8)),color="gray60",size=2,alpha=0.5, shape=18) + 
    geom_point2(aes(subset=(!isTip & as.numeric(posterior)>0.91)),color="gray40",size=3,shape=18,alpha=0.5) + 
    geom_point2(aes(subset=(!isTip & as.numeric(posterior)>=0.96)),color="black",size=3,shape=18,alpha=0.5)
  ######
  # extract 95% HPD intervals - geom_range seems unable to do correctly with this tree (known bug for tip dated trees), so extract data and plot using geom_segment
  TPA.beast.subtree.test.data <- fortify(my.beast.tree)
  minmax <- t(matrix(unlist(TPA.beast.subtree.test.data[!is.na(TPA.beast.subtree.test.data$height_0.95_HPD),"height_0.95_HPD"]),nrow=2))
  bar_df <- data.frame(node_id=TPA.beast.subtree.test.data[!is.na(TPA.beast.subtree.test.data$height_0.95_HPD),"node"],as.data.frame(minmax))
  names(bar_df) <- c('node_id','min','max') 
  bar_df <- bar_df %>% filter(node_id > Ntip(my.beast.tree@phylo))
  bar_df <- bar_df %>% left_join(TPA.beast.subtree.test.data, by=c('node_id'='node')) #%>% select(node_id,min,max,y)
  #mrcd.decimal <- decimal_date(mrsd.Beast.tree.test)
  mrcd.decimal <- decimal_date(mrsd.Beast.tree.fulltree)
  
  # Now add HPDs to plot
  p.TPA.beast.subtree.test <- p.TPA.beast.subtree.test + geom_segment(aes(x=mrcd.decimal-max, y=y, xend=mrcd.decimal-min, yend=y), data=bar_df, color='red', alpha=0.2, size=2.0)
  # Output tree 
  return(p.TPA.beast.subtree.test)
}
################################################################################################


################################################################################################
# Function to add metadata to tree
# Has two optional arguments "initial.track.offset" and "track.scaling" which can be used to alter the width and positioning of metadata tracks

plot_beast_subtree_with_PHE_metadata <- function(my.beast.tree.input, my.metadata, my.phe.meta, initial.track.offset, track.scaling){
    # Add code to allow scaling up of the track offsets and widths - useful for much bigger length trees
  if(missing(initial.track.offset)){
    initial.track.offset <- 0
  }    
  if(missing(track.scaling)){
    track.scaling <- 1
  }
  # Calculate amount to offset each heatmap track
  offset.dist <- 4*track.scaling
  track.width <- (1/max(my.beast.tree.input$data$height)*3)*track.scaling
  
  # make a list of taxa used in this plot 
  my.taxa.list <- as.character(unlist(filter(my.beast.tree.input$data, isTip==TRUE) %>% select(label)))
  
  # make a color scale for sampling years
  #PHE.sublintest.year.cols <- data.frame(year=sort(unique(as.numeric(unlist(my.metadata[(my.metadata$Sample_Name %in% my.taxa.list),"Sample_Year"],use.names=F)))),stringsAsFactors = T)
  #PHE.sublintest.year.cols$year.cols <- colorRampPalette(brewer.pal(7, "YlOrRd"))(nrow(PHE.sublintest.year.cols))
  
  # Or alternatively, use a common colour scheme for all data (maybe more sensible)
  PHE.sublintest.year.cols <- data.frame(year=TPA.year.cuttoff.cols$date.cuttoff, year.cols=TPA.year.cuttoff.cols$date.cuttoff.col, stringsAsFactors = F)
  
  # make metadata file for UK regions present in sublineage
  sublin.test.region.meta <- data.frame(row.names=as.character(unlist(my.phe.meta[my.phe.meta$Sample_Name %in% my.taxa.list,"Sample_Name"])), Region=as.character(unlist(my.phe.meta[my.phe.meta$Sample_Name %in% my.taxa.list,"phe_centre"])), stringsAsFactors = F)
  
  # Add heatmap strips
  # Sample Year
  #TPA.beast.subtree.test.global.plot1.regional <- gheatmap(my.beast.tree.input, TPA.rawseq.all.Years.p, color=NULL,width=track.width, offset=initial.track.offset+offset.dist,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
    #scale_fill_manual(name="Year", values=PHE.sublintest.year.cols$year.cols,breaks=PHE.sublintest.year.cols$year, guide = guide_legend(order = 1, ncol=2)) +
    #ggnewscale::new_scale_fill()
  TPA.beast.subtree.test.global.plot1.regional <- gheatmap(my.beast.tree.input, TPA.rawseq.year.cuttoff.p, color=NULL,width=track.width, offset=initial.track.offset+offset.dist,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) +
    scale_fill_manual(name="Year", values=PHE.sublintest.year.cols$year.cols,breaks=PHE.sublintest.year.cols$year, guide = guide_legend(order = 1, ncol=2)) +
    ggnewscale::new_scale_fill()
  
  # Add country
  TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, TPA.rawseq.countries.p, color=NULL,width=track.width, offset=initial.track.offset+(offset.dist*2),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="Country", values=continental.country.cols.brew2$country.col, breaks=continental.country.cols.brew2$Geo_Country, guide = guide_legend(order = 2)) +
    ggnewscale::new_scale_fill()
  # UK or non-UK
  TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional,
                                                           TPA.rawseq.UK.p, color=NULL,width=track.width,offset=initial.track.offset+(offset.dist*3), colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=theme.text.size.within) + 
    scale_fill_manual(name="England/Other", breaks=c("England","Other"), values=c("black","grey95"), na.value = "white", guide = guide_legend(order = 3, ncol=2)) +
    ggnewscale::new_scale_fill()
  # UK PHE region
  TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, sublin.test.region.meta, color=NULL,width=track.width, offset=initial.track.offset+(offset.dist*4),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="UKHSA Region", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region, na.value = "white", guide = guide_legend(order = 4)) +
    ggnewscale::new_scale_fill()
  
  # TPA sublineage
  #TPA.beast.subtree.test.global.plot1.regional <- gheatmap(TPA.beast.subtree.test.global.plot1.regional, data.frame(row.names=TPA.meta2.1$Sample_Name, Sublineage=TPA.meta2.1$TPA.pinecone.sublineage, stringsAsFactors = F), color=NULL,width=track.width,offset=initial.track.offset+(offset.dist*5), colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0,font.size=2.5) + 
  #scale_fill_manual(name="Sublineage",values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, guide = guide_legend(order = 5)) 
  
  TPA.beast.subtree.test.global.plot1.regional <- TPA.beast.subtree.test.global.plot1.regional + theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
    new_scale_fill() +
    geom_rootedge(2) +
    NULL
  
  # calculate number of taxa
  test.taxacount <- length(my.taxa.list)
  # Adjust final plot x and y axis to make space for labels using taxa counts
  x.axis.limits <- ggplot_build(TPA.beast.subtree.test.global.plot1.regional)$layout$panel_scales_x[[1]]$range$range
  TPA.beast.subtree.test.global.plot1.regional <- TPA.beast.subtree.test.global.plot1.regional + 
    coord_cartesian(y=c(-0.5-(test.taxacount/15),test.taxacount+2), x=c(x.axis.limits[1],x.axis.limits[2]+3))
  
  return(TPA.beast.subtree.test.global.plot1.regional)
}
################################################################################################


Great, now let’s plot a full beast tree

# function for x-axis time breaks needs tweaking for the full tree
TPA.Global.full.BeastTree.ukmeta <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(my.beast.tree = full.beast2.tree, my.metadata = TPA.meta2.1, my.phe.meta = PHE.metadata.linked, mrsd.fulltree = "2019-06-01") + scale_x_continuous(breaks=seq(1400,2020,50), minor_breaks=seq(1950, 2020, 5)), my.metadata = TPA.meta2.1, my.phe.meta = PHE.metadata.linked, track.scaling = 5)
Scale for 'x' is already present. Adding another scale for 'x', which will replace the existing
scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
TPA.Global.full.BeastTree.ukmeta


#ggsave(paste0(Figure_output_directory,"SupFig7_TPA_FullBeastTree.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=185, height=240, device='pdf', dpi=1200)


Now do sublineage plots
Make some plots

# Sublineage 1
sublineage.1.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 1), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.2)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Sublineage.2
sublineage.2.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 2), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Sublineage.8
sublineage.8.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 8), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.1)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Sublineage.14
sublineage.14.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, track.scaling = 1.1)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.


Plot together?
Maybe with sublineage 1 expanded?

p.beast.trees.heatmap.sublineages.combi.offset1 <- plot_grid(sublineage.2.tree.heatmap, 
          sublineage.8.tree.heatmap, 
          sublineage.14.tree.heatmap, 
          ncol=2, labels=c("B - Sublineage 2","C - Sublineage 8","D - Sublineage 14"), label_size=panel.lab.size, scale=0.95, vjust=1.0)

p.beast.trees.heatmap.sublineages.combi.offset2 <- plot_grid(sublineage.1.tree.heatmap, p.beast.trees.heatmap.sublineages.combi.offset1, labels=c("A - Sublineage 1", ""), label_size=panel.lab.size, scale=0.975, ncol=2, rel_widths=c(6,11), vjust=2.5)


p.beast.trees.heatmap.sublineages.combi.offset2

#ggsave(paste0(Figure_output_directory,"SupFig8_TPA-PHE_Sublineage-BeastTrees.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=265, height=230, device='pdf', dpi=1200)


Need to explore sublineage 14 a bit more to get dates for those subclades

sublineage.14.tree.heatmap + geom_tiplab(size=theme.text.size.within, linesize=0.4) #3


# Ok, there are multiple subclades in this tree
sublineage.14.tree.heatmap.data <- sublineage.14.tree.heatmap$data

# getMRCA(full.beast2.tree@phylo,c("PHE150150A","NL14","TPA_BCC122","TPA_BCC126","PHE140076A","TPA_UKBRG008"))  982
# full.beast2.tree@phylo$tip.label[phangorn::Descendants(full.beast2.tree@phylo, 982, type = c("tips"))[[1]]]

sublineage.14.lowerclade.list <- c("NL17", "NL19", "PHE140085A", "PHE140089A", "PHE150118A", "PHE150121A", "PHE150133A", "PHE150143A", "PHE150145A", "PHE150162A", "PHE150166A", "PHE150168A", "PHE160224A", "PHE160243A", "PHE160255A", "PHE160276A", "PHE160290A", "PHE160302A", "PHE160306A", "PHE170333A", "PHE170349A", "PHE170374A", "PHE170381A", "PHE170664A", "TPA_ESBCN005", "TPA_UKBIR032")

sublineage.14.upperclade.list <- c("NL14", "PHE140076A", "PHE150149A", "PHE150150A", "PHE150170A", "PHE160196A", "PHE160263A", "PHE160274A", "PHE160287A", "PHE160294A", "PHE160316A", "PHE160317A", "PHE170372A", "PHE170386A", "PHE170397A", "PHE170405A", "TPA_BCC081", "TPA_BCC088", "TPA_BCC089", "TPA_BCC101", "TPA_BCC122", "TPA_BCC126", "TPA_BCC136", "TPA_BCC169", "TPA_HUN180004", "TPA_HUN190020", "TPA_UKBIR044", "TPA_UKBRG007", "TPA_UKBRG008")

# Get MRCA date for lower clade
sublineage.14.lowerclade.list.tmrca <- sublineage.14.tree.heatmap.data[sublineage.14.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14)@phylo, sublineage.14.lowerclade.list),"x"]

paste0("TMRCA for sublineage 14 lower clade: ",sublineage.14.lowerclade.list.tmrca)
[1] "TMRCA for sublineage 14 lower clade: 2006.53850498154"
# Get MRCA date for upper clade
sublineage.14.upperclade.list.tmrca <- sublineage.14.tree.heatmap.data[sublineage.14.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 14)@phylo, sublineage.14.upperclade.list),"x"]

paste0("TMRCA for sublineage 14 upper clade: ",sublineage.14.upperclade.list.tmrca)
[1] "TMRCA for sublineage 14 upper clade: 1999.15025243934"



Extract key information for sublineage 6 (two samples)

sublineage.6.tree.heatmap <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 6), TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
sublineage.6.tree.heatmap.data <- sublineage.6.tree.heatmap$data

# Get MRCA date for upper clade
sublineage.6.beasttree.tmrca <- as.numeric(sublineage.6.tree.heatmap.data[sublineage.6.tree.heatmap.data$node==getMRCA(Extract_sublineage_tree_for_plot(full.beast2.tree, TPA.meta2.1, PHE.metadata.linked, my.sublineage = 6)@phylo, c("PHE130048A", "PHE160283A")),"branch"])


paste0("TMRCA for sublineage 6 upper clade: ",sublineage.6.beasttree.tmrca)
[1] "TMRCA for sublineage 6 upper clade: 1982.61865062176"



### Extract sample & population statistics from datasets for use in manuscript text
Dataset and Geographical distributions

# dataset counts
paste0("Total UK samples in cleaned/deduplicated dataset: ",nrow(PHE.metadata.linked))
[1] "Total UK samples in cleaned/deduplicated dataset: 237"
paste0("Of which: ",nrow(PHE.metadata.linked[PHE.metadata.linked$is.PHE=="PHE",])," from PHE Ref lab at Colindale")
[1] "Of which: 195 from PHE Ref lab at Colindale"
paste0("Of which: ",nrow(PHE.metadata.linked[PHE.metadata.linked$is.PHE=="Other",])," from other labs")
[1] "Of which: 42 from other labs"
# proportion with geographical data
paste0("From UK samples, ", nrow(PHE.metadata.linked[(PHE.metadata.linked$phe_centre %notin% c("Not Known","UK (not England)")),])," were grouped into one of the 9 PH regions")
[1] "From UK samples, 217 were grouped into one of the 9 PH regions"
paste0("From UK samples, ", nrow(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="UK (not England)",]), " were referred from outside England")
[1] "From UK samples, 2 were referred from outside England"
paste0("From UK samples, ", nrow(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="Not Known",]), " had unknown region")
[1] "From UK samples, 18 had unknown region"
# counts & fractions by PHE region
PHE.geo.count
NA


Gender Orientation stats

PHE.orientation.counts
PHE.geo.orientation.counts
PHE.geo.HIV.counts
PHE.sublineage.orientation.counts
PHE.sublineage.Age


Sublineage Distributions

PHE.Lineage.count
PHE.sublin.count
PHE.geo.sublineage


Macrolide resistance stats

UK.macrolide.res <- PHE.metadata.linked %>%
  dplyr::group_by(A2058G, A2059G) %>%
  dplyr::summarise(Count.allele=n()) %>%
  dplyr::ungroup() %>%
  dplyr::mutate(total.count=sum(Count.allele), perc.allele=round((Count.allele/total.count)*100,1))
`summarise()` has grouped output by 'A2058G'. You can override using the `.groups` argument.
UK.macrolide.res

UK.macrolide.res.sublin <- PHE.metadata.linked %>%
  dplyr::group_by(TPA.pinecone.sublineage, A2058G, A2059G) %>%
  dplyr::summarise(Count.allele=n()) %>%
  dplyr::ungroup() %>%
  dplyr::group_by(TPA.pinecone.sublineage) %>%
  dplyr::mutate(total.count=sum(Count.allele), perc.allele=round((Count.allele/total.count)*100,1))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'A2058G'. You can override using the `.groups` argument.
UK.macrolide.res.sublin


# Calculate long form df, with different 23S alleles (A2058G, A2059G, WT, Uncertain) v.s. sublineage
UK.macrolide.res.sublin.long <- PHE.metadata.linked %>%
  mutate(Resistance.allele=ifelse(A2058G=="Yes", "A2058G", ifelse(A2059G=="Yes", "A2059G", ifelse((A2058G=="No" & A2059G=="No"),"Wild Type", "Uncertain")))) %>%
  dplyr::group_by(TPA.pinecone.sublineage, Resistance.allele) %>%
  dplyr::summarise(Count.per.sublin.Macrolides=n()) %>%
  dplyr::mutate(total.sublin=sum(Count.per.sublin.Macrolides), 
                fraction=Count.per.sublin.Macrolides/total.sublin) %>%
  #dplyr::ungroup() %>%
  dplyr::arrange((Resistance.allele), .by_group=T) %>%
  dplyr::mutate(cum_fract = cumsum(fraction)) %>%
  dplyr::mutate(cum_fract.mid = cum_fract-(fraction/2)) %>%
  dplyr::mutate(Resistance.allele = factor(Resistance.allele, levels=rev(c("A2058G", "A2059G", "Uncertain", "Wild Type"))))
`summarise()` has grouped output by 'TPA.pinecone.sublineage'. You can override using the `.groups` argument.
# Make plot of macrolide resistance by sublineages
p.sublin.Macrolides.hbarplot <- ggplot(UK.macrolide.res.sublin.long, aes(Count.per.sublin.Macrolides, y=TPA.pinecone.sublineage, fill=Resistance.allele)) +
  geom_barh(stat="identity", position="fill", width=0.65) +
  theme_light() +
  scale_fill_manual(name="Macrolide\nResistance\nAllele",values=c("indianred2", "steelblue1","grey55", "grey90"), breaks=c("A2058G", "A2059G", "Uncertain", "Wild Type")) +
  labs(y="TPA Sublineage", x="Proportion with Macrolide Resistance Allele") +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='bottom') +
  guides(fill=guide_legend(ncol=2)) +
  geom_text(data=UK.macrolide.res.sublin.long, aes(cum_fract.mid, y=TPA.pinecone.sublineage,label=Count.per.sublin.Macrolides), size=theme.text.size.within, inherit.aes = F) +
  NULL

p.sublin.Macrolides.hbarplot



# Combine plot with sublineage count bars
p.sublin.Macrolides.hbarplot.combi <- plot_grid(p.sublineage.hbarplot + guides(fill=guide_legend(ncol=3)), p.sublin.Macrolides.hbarplot + y.theme.strip, nrow=1, align=T, labels=c("A", "B"), label_size=panel.lab.size)

p.sublin.Macrolides.hbarplot.combi


#ggsave(paste0(Figure_output_directory,"SupFig9_TPA-PHE_Sublin-Macrolide-Res.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=160, height=120, device='pdf', dpi=1200)



Pairwise SNP analysis


OK, want to investigate the different patterns observable for the North East of England (pale blue) in Sublineage 1
Multiple ways we can do this - including SNP distances (also multiple ways to do that)

###
#Use phylogenetic distance from the SNP scaled tree
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist <- ape::cophenetic.phylo(TPA.pyjar.tree.subset.uk)
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt <- data.frame(Taxa1=row.names(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist), TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist, stringsAsFactors = F) %>% tidyr::gather(Taxa2, Distance.Phylo, -Taxa1)
# Taxa Comparisons label
TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa_combination <- sapply(1:nrow(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt), function (x) paste0(sort(c(as.character(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa1[x]),as.character(TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt$Taxa2[x]))),collapse="___"))
# Merge together
#TPA.WGS.alignment.data.dist.melt <- dplyr::left_join(TPA.WGS.alignment.data.dist.melt, TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt[,c("Taxa_combination","Distance.Phylo")], by="Taxa_combination")

TPA.WGS.alignment.data.dist.melt <- TPA.pyjar.tree.subset.uk.cophenetic.SNP.dist.melt


TPA.WGS.alignment.data.dist.melt <- unique(TPA.WGS.alignment.data.dist.melt)


Ok, now bring in some metadata and comparisons

# Bring in and merge metadata
PHE.meta.pairwise.t1 <- PHE.metadata.linked[,c("Sample_Name","year","phe_centre","london","gender_orientation","hivpos","age_group","ukborn","TPA.pinecone.sublineage", "TPA_Lineage","Geo_Country","is.UK","is.PHE", "Sample_Year","date.decimal")]

colnames(PHE.meta.pairwise.t1) <- paste0(colnames(PHE.meta.pairwise.t1),".t1")
colnames(PHE.meta.pairwise.t1)[1] <- "Taxa1"
PHE.meta.pairwise.t2 <- PHE.metadata.linked[,c("Sample_Name","year","phe_centre","london","gender_orientation","hivpos","age_group","ukborn","TPA.pinecone.sublineage", "TPA_Lineage","Geo_Country","is.UK","is.PHE", "Sample_Year","date.decimal")]
colnames(PHE.meta.pairwise.t2) <- paste0(colnames(PHE.meta.pairwise.t2),".t2")
colnames(PHE.meta.pairwise.t2)[1] <- "Taxa2"

PHE.alignment.data.dist.melt.meta <- plyr::join(TPA.WGS.alignment.data.dist.melt,PHE.meta.pairwise.t1, by="Taxa1", type="left") 
PHE.alignment.data.dist.melt.meta <- plyr::join(PHE.alignment.data.dist.melt.meta,PHE.meta.pairwise.t2, by="Taxa2", type="left")

# Exclude missing data (e.g. missing sublineage) - this will also remove non-UK samples, since full metadata is missing here
PHE.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[!is.na(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1),]
PHE.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[!is.na(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2),]


Define comparisons

# Same sample
PHE.alignment.data.dist.melt.meta$same.sample <- ifelse(PHE.alignment.data.dist.melt.meta$Taxa1==PHE.alignment.data.dist.melt.meta$Taxa2,"same", "different")

# Years between samples
PHE.alignment.data.dist.melt.meta$year.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$year.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$year.t2))

PHE.alignment.data.dist.melt.meta$Sample_Year.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$Sample_Year.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$Sample_Year.t2))

# Years between decimal date (more precise temporal distance)
PHE.alignment.data.dist.melt.meta$decimal.date.distance <- abs(as.numeric(PHE.alignment.data.dist.melt.meta$date.decimal.t1) - as.numeric(PHE.alignment.data.dist.melt.meta$date.decimal.t2))

# Epidemiological time between - catagorical
PHE.alignment.data.dist.melt.meta$epi.time.distance.cat <- ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<1/12,"month", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3/12, "quarter", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6/12, "half year", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=1, "1 year",ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=2, "2 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3, "3 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=4, "4 years", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=5, "5 years",  ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6, "6 years",">6 years")))))))))

PHE.alignment.data.dist.melt.meta$epi.time.distance.cat <- factor(PHE.alignment.data.dist.melt.meta$epi.time.distance.cat, levels=c("month", "quarter","half year","1 year", "2 years", "3 years", "4 years", "5 years", "6 years", ">6 years"))

PHE.alignment.data.dist.melt.meta$epi.time.distance.cat.years <- ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=1, "0", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=2, "1", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=3, "2", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=4, "3", ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=5, "4",  ifelse(PHE.alignment.data.dist.melt.meta$decimal.date.distance<=6, "5",">5"))))))


# Same country
PHE.alignment.data.dist.melt.meta$same.country <- ifelse(PHE.alignment.data.dist.melt.meta$Geo_Country.t1 == PHE.alignment.data.dist.melt.meta$Geo_Country.t2, "same", "different")

# Is UK
PHE.alignment.data.dist.melt.meta$both.uk <- ifelse(PHE.alignment.data.dist.melt.meta$is.UK.t1 == PHE.alignment.data.dist.melt.meta$is.UK.t2, "same", "different")

# Is PHE
PHE.alignment.data.dist.melt.meta$both.PHE <- ifelse(PHE.alignment.data.dist.melt.meta$is.PHE.t1 == PHE.alignment.data.dist.melt.meta$is.PHE.t2, "same", "different")

# Same TPA Lineage (cleaned up classifications)
PHE.alignment.data.dist.melt.meta$same.TPA.Lineage <- ifelse(PHE.alignment.data.dist.melt.meta$TPA_Lineage.t1==PHE.alignment.data.dist.melt.meta$TPA_Lineage.t2, "same", "different")
PHE.alignment.data.dist.melt.meta$same.TPA.Lineage <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function(x) ifelse((PHE.alignment.data.dist.melt.meta$TPA_Lineage.t1[x]=="0" | PHE.alignment.data.dist.melt.meta$TPA_Lineage.t2[x]=="0"),NA,PHE.alignment.data.dist.melt.meta$same.TPA.Lineage[x]))

# Same TPA sublineage
PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster <- ifelse(PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1==PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2,"same", "different")
PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function(x) ifelse(((PHE.alignment.data.dist.melt.meta$same.sample[x]=="different" & PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t1[x]=="Singleton") |(PHE.alignment.data.dist.melt.meta$same.sample[x]=="different" & PHE.alignment.data.dist.melt.meta$TPA.pinecone.sublineage.t2[x]=="Singleton")),"different",PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster[x]))

# Define Genetic relationships hierarchically
PHE.alignment.data.dist.melt.meta$genomic.cluster.hierarchy <- ifelse(PHE.alignment.data.dist.melt.meta$Distance==0,"Zero_SNPs", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster=="same","Same Sublineage", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Lineage=="same", "Same Lineage","Different Lineage")))

PHE.alignment.data.dist.melt.meta$genomic.cluster.hierarchy.ph <- ifelse(PHE.alignment.data.dist.melt.meta$Distance.Phylo==0,"Zero_SNPs", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Pinecone.cluster=="same","Same Sublineage", ifelse(PHE.alignment.data.dist.melt.meta$same.TPA.Lineage=="same", "Same Lineage","Different Lineage")))


# Same PHE region
PHE.alignment.data.dist.melt.meta$same.PHE.region <- ifelse(PHE.alignment.data.dist.melt.meta$phe_centre.t1==PHE.alignment.data.dist.melt.meta$phe_centre.t2, "same", "different")
PHE.alignment.data.dist.melt.meta$PHE.centre.combination <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) paste0(sort(c(as.character(PHE.alignment.data.dist.melt.meta$phe_centre.t1[x]),as.character(PHE.alignment.data.dist.melt.meta$phe_centre.t2[x]))),collapse="___"))

# does the combination included London?
PHE.alignment.data.dist.melt.meta$involves.London <- ifelse(PHE.alignment.data.dist.melt.meta$phe_centre.t1=="London" | PHE.alignment.data.dist.melt.meta$phe_centre.t2=="London", "London", "not-London")


# Orientation pair
PHE.alignment.data.dist.melt.meta$Orientation_combination <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) paste0(sort(c(as.character(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]),as.character(PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]))),collapse="___"))

#PHE.alignment.data.dist.melt.meta$Orientation.Class <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSM", "MSM",
#       ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSM" | PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSM", "Mixed", 
#              ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="MSW" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="WSM","Heterosexual", 
#                     ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="WSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="MSW","Heterosexual","Unknown")))))

PHE.alignment.data.dist.melt.meta$Orientation.Class <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta), function (x) ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x]=="GBMSM", "GBMSM",
                                                                                                                             ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x] %in% c("MSW","WSM") & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x] %in% c("MSW","WSM"),"Heterosexual",
                                                                                                                                    ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM" & PHE.alignment.data.dist.melt.meta$gender_orientation.t2[x] %in% c("MSW","WSM"), "Mixed", 
                                                                                                                                           ifelse(PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x] %in% c("MSW","WSM") & PHE.alignment.data.dist.melt.meta$gender_orientation.t1[x]=="GBMSM", "Mixed", "Unknown")))))
                    


# Country Comparisons label
PHE.alignment.data.dist.melt.meta$Country_combinations <- paste0(PHE.alignment.data.dist.melt.meta$Geo_Country.t1,"___",PHE.alignment.data.dist.melt.meta$Geo_Country.t2)

# Subset to PHE data only (effectively already done, but let's be explicit)
PHE.TPA.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$both.uk=="same" &  PHE.alignment.data.dist.melt.meta$both.PHE=="same"),]
PHE.TPA.alignment.data.dist.melt.meta <- PHE.TPA.alignment.data.dist.melt.meta[PHE.TPA.alignment.data.dist.melt.meta$PHE.only=="PHE",]

PHE.TPA.alignment.data.dist.melt.meta <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$both.uk=="same"),]



# Make single sided
PHE.TPA.alignment.data.dist.melt.meta <- PHE.TPA.alignment.data.dist.melt.meta[!duplicated(PHE.TPA.alignment.data.dist.melt.meta$Taxa_combination),]



### Perform a more detailed analysis of samples from the North East of England
Do a more detailed exploration of the North East of England

PHE.metadata.linked2.region_NorthEast <- PHE.metadata.linked[PHE.metadata.linked$phe_centre=="North East",]

# Constrain by samples being from the North East
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta[(PHE.alignment.data.dist.melt.meta$phe_centre.t1=="North East" & PHE.alignment.data.dist.melt.meta$same.sample=="different"),]

# Constrain by the same PHE region
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[PHE.alignment.data.dist.melt.meta.NorthEast.clusters$same.PHE.region=="same",]

#Just plot these distros
p.NorthEast.Pairwise.SNPs.unconstrained <- ggplot(PHE.alignment.data.dist.melt.meta.NorthEast.clusters, aes(Distance.Phylo)) + 
  geom_histogram(binwidth = 1) +
  theme_bw() +
  theme.text.size +
  labs(x="Pairwise SNP Distance", y="Comparison Count")

p.NorthEast.Pairwise.SNPs.unconstrained


Make a single linkage network from the North East samples


# Constrain by SNP distance (looser than previously - we just want to find basic groupings within sublineage 1 for NE samples)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[PHE.alignment.data.dist.melt.meta.NorthEast.clusters$Distance.Phylo<=2,]

# And make sure that we actually have genetic distance data for all samples within the network
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[!is.na(PHE.alignment.data.dist.melt.meta.NorthEast.clusters$Distance.Phylo),]

# cleanup some data noise
PHE.alignment.data.dist.melt.meta.NorthEast.clusters <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[!is.na(PHE.alignment.data.dist.melt.meta.NorthEast.clusters$year.t1),]

# prepare intput data (with edge info)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1 <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters[,c("Taxa1","Taxa2","Distance.Phylo","decimal.date.distance","year.distance","Orientation.Class","epi.time.distance.cat")]

############
# some issues with update to R4 - double sided matrix
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$edgename <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1), function(x) paste0(sort(as.character(unlist(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1[x,c("Taxa1","Taxa2")]))),collapse="___"))
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1 <- PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1[!duplicated(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$edgename),]

# Also having an issue with taxa as factors here
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa1 <- as.character(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa1)
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa2 <- as.character(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Taxa2)
############

#inverse weight
PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Distance.inv <- 1/PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1$Distance.Phylo

# Make actual network
set.seed(1235)
PHE.NorthEast.network <- network(PHE.alignment.data.dist.melt.meta.NorthEast.clusters.input1, matrix.type = "edgelist", ignore.eval = FALSE, directed = F)

PHE.NorthEast.network.gg <- ggnetwork(PHE.NorthEast.network, layout = "kamadakawai", weights = "Distance.inv")
PHE.NorthEast.network.gg$Taxa1 <- PHE.NorthEast.network.gg$vertex.names

# extract temporal clusters from network
PHE.NorthEast.network.ig <- asIgraph(PHE.NorthEast.network)
PHE.NorthEast.network.components <- data.frame(Taxa1=network.vertex.names(PHE.NorthEast.network), vertex.no=as.vector(V(PHE.NorthEast.network.ig)), cluster=igraph::components(PHE.NorthEast.network.ig)$membership)
# For ease of story telling in the paper, flip clusters 2 and 3 around (so we can talk about 2 first)
PHE.NorthEast.network.components <- PHE.NorthEast.network.components %>%
  dplyr::mutate(cluster.old=cluster, cluster=ifelse(cluster.old==2, 3, ifelse(cluster.old==3,2,cluster.old)))
PHE.NorthEast.network.components$Cluster <- paste0("Cluster",PHE.NorthEast.network.components$cluster)

# merge metadata back in
PHE.NorthEast.network.gg <- plyr::join(PHE.NorthEast.network.gg, data.frame(Taxa1=PHE.metadata.linked$Sample_Name, PHE.metadata.linked[,c("phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], stringsAsFactors = F),by="Taxa1", type="left")

PHE.NorthEast.network.gg <- plyr::join(PHE.NorthEast.network.gg, data.frame(Taxa1=PHE.NorthEast.network.components$Taxa1, Cluster=PHE.NorthEast.network.components$Cluster), by="Taxa1", type="left")


Plot network

# Plot network
p.PHE.NorthEast.network.2SNP <- ggplot(PHE.NorthEast.network.gg, aes(x = x, y = y, xend = xend, yend = yend)) + 
  geom_edges(alpha=0.90, curvature = 0.2, aes(color=factor(Distance.Phylo), linetype=factor(Distance.Phylo))) +
  scale_color_manual(values=c("grey5","grey55","grey85"), name="SNP\nDistance") +
  scale_linetype(name="SNP\nDistance") +
  theme_blank() +
  ggnewscale::new_scale_color() + ggnewscale::new_scale("size") +
  geom_nodelabel(aes(color=gender_orientation, label=paste(Taxa1,year,sep="\n"),fontface = "bold"), alpha=0.8, size=theme.text.size.within-0.4, label.size=0.15, label.padding = unit(0.05, "lines")) +
  geom_nodes(size=1.0, aes(color=gender_orientation)) + 
  scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) + 
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  NULL
p.PHE.NorthEast.network.2SNP


Ok, so three networks. Clear differentiation of a heterosexual network (with 0-snp distances) and two predominantly MSM networks
Let’s look at the phylogenetic context of those North East clusters we’ve defined. Pull out subtrees (from sublineage 1 subtree)

# Cluster 1
Beast.tree.NE.cluster1 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster1","Taxa1"])
Beast.tree.NE.cluster1.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster1, levels_back=0)

p.Beast.tree.NE.cluster1.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster1.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 10)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Can't fit in tip labs, but since this is a polyphyletic subtree, it would be helpful to add a track to highlight the NE strains
PHE.metadata.linked$is.NorthEast <- ifelse(PHE.metadata.linked$phe_centre=="North East","North East", "Other England")
p.Beast.tree.NE.cluster1.subtree.cluster.highlight <- gheatmap(p.Beast.tree.NE.cluster1.subtree, data.frame(row.names=PHE.metadata.linked$Sample_Name, `North East`=PHE.metadata.linked$is.NorthEast), color=NULL,width=(1/max(p.Beast.tree.NE.cluster1.subtree$data$height)*3), offset=10+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nEngland", values=c("#A6CEE3","grey95"), breaks=c("North East","Other England"), na.value = "white", guide = guide_legend(order = 5)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Just confirm the ClusterIDs for this subtree (make sure it doesn't enclose other clusters)
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID <- gheatmap(p.Beast.tree.NE.cluster1.subtree.cluster.highlight, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster1.subtree$data$height)*3), offset=10+(4*6),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 6)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID <- p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID + 
    coord_cartesian(x=c(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits[1],p.Beast.tree.NE.cluster1.subtree.cluster.highlight.x.axis.limits[2]+4), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster1.subtree.cluster.highlight$data$label))/15),length(unique(p.Beast.tree.NE.cluster1.subtree.cluster.highlight$data$label))+2)) +
  theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID

#######################
# Cluster 2
Beast.tree.NE.cluster2 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster2","Taxa1"])
Beast.tree.NE.cluster2.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster2, levels_back=1)

p.Beast.tree.NE.cluster2.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster2.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 20) + geom_tiplab(size=theme.text.size.within, align=T, offset=5, linesize=0.4)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Just add ClusterIDs for this subtree to highlight
p.Beast.tree.NE.cluster2.subtree <- gheatmap(p.Beast.tree.NE.cluster2.subtree, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster2.subtree$data$height)*3), offset=20+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 5, ncol=2)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster2.subtree.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster2.subtree)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster2.subtree <- p.Beast.tree.NE.cluster2.subtree + 
    coord_cartesian(x=c(p.Beast.tree.NE.cluster2.subtree.x.axis.limits[1],p.Beast.tree.NE.cluster2.subtree.x.axis.limits[2]+12), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster2.subtree$data$label))/20)-1,length(unique(p.Beast.tree.NE.cluster2.subtree$data$label))+0.5)) + 
  theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster2.subtree

############################
# Cluster 3
Beast.tree.NE.cluster3 <- getMRCA(full.beast2.tree@phylo, PHE.NorthEast.network.components[PHE.NorthEast.network.components$Cluster=="Cluster3","Taxa1"])
Beast.tree.NE.cluster3.subtree <- tree_subset(full.beast2.tree, node=Beast.tree.NE.cluster3, levels_back=1)

p.Beast.tree.NE.cluster3.subtree <- plot_beast_subtree_with_PHE_metadata(plot_beast_subtree_with_HPD(Beast.tree.NE.cluster3.subtree, TPA.meta2.1, PHE.metadata.linked,"2019-06-01"), TPA.meta2.1, PHE.metadata.linked, initial.track.offset = 26) + geom_tiplab(size=theme.text.size.within, align=T, offset=3, linesize=0.4)
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Just add ClusterIDs for this subtree to highlight
p.Beast.tree.NE.cluster3.subtree <- gheatmap(p.Beast.tree.NE.cluster3.subtree, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.NE.cluster3.subtree$data$height)*3), offset=26+(4*5),colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 5, ncol=2)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# add a bit more room to the x axis
p.Beast.tree.NE.cluster3.subtree.x.axis.limits <- ggplot_build(p.Beast.tree.NE.cluster3.subtree)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.NE.cluster3.subtree <- p.Beast.tree.NE.cluster3.subtree + 
    coord_cartesian(x=c(p.Beast.tree.NE.cluster3.subtree.x.axis.limits[1],p.Beast.tree.NE.cluster3.subtree.x.axis.limits[2]+12), y=c(-0.5-(length(unique(p.Beast.tree.NE.cluster3.subtree$data$label))/20)-1,length(unique(p.Beast.tree.NE.cluster3.subtree$data$label))+0.5)) + 
  theme(legend.margin = margin(-0.5,0,0,0, unit="mm"))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
#p.Beast.tree.NE.cluster3.subtree

#p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID
#p.Beast.tree.NE.cluster2.subtree 
#p.Beast.tree.NE.cluster3.subtree 


Since Cluster 1 is really quite polyphyletic, it maybe more useful to show the clusters in context for that one

# Add North East identifier column
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- gheatmap(sublineage.1.tree.heatmap, data.frame(row.names=PHE.metadata.linked$Sample_Name, `North East`=PHE.metadata.linked$is.NorthEast), color=NULL,width=(1/max(sublineage.1.tree.heatmap$data$height)*3)*1.2, offset=0+(4*5)*1.2,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nEngland", values=c("#A6CEE3","grey95"), breaks=c("North East","Other England"), na.value = "white", guide = guide_legend(order = 5)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# Just confirm the ClusterIDs for this subtree (make sure it doesn't enclose other clusters)
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- gheatmap(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight, data.frame(row.names=PHE.NorthEast.network.components$Taxa1, ClusterID=PHE.NorthEast.network.components$Cluster), color=NULL,width=(1/max(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$height)*3)*1.2, offset=0+(4*6)*1.2,colnames_angle=-45,colnames_offset_y=0.02, hjust=-0.0, font.size=theme.text.size.within) + 
    scale_fill_manual(name="North East\nCluster", values=c("#7fc97f","#beaed4","#fdc086"), breaks=c("Cluster1","Cluster2","Cluster3"), na.value = "white", guide = guide_legend(order = 6, ncol=2)) +
    ggnewscale::new_scale_fill()
Scale for 'fill' is already present. Adding another scale for 'fill', which will replace the
existing scale.
# add a bit more room to the x axis
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits <- ggplot_build(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight)$layout$panel_scales_x[[1]]$range$range
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- p.Beast.tree.sublineage1.NE.subtree.cluster.highlight + 
    coord_cartesian(x=c(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits[1],p.Beast.tree.sublineage1.NE.subtree.cluster.highlight.x.axis.limits[2]+4), y=c(-0.5-(length(unique(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$label))/15),length(unique(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight$data$label))+2))
Coordinate system already present. Adding new coordinate system, which will replace the existing one.
# reduce spacing between legend scales
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight <- p.Beast.tree.sublineage1.NE.subtree.cluster.highlight + theme(legend.margin = margin(-0.95,0,0,0, unit="mm"))
p.Beast.tree.sublineage1.NE.subtree.cluster.highlight

  Plot together


p.Beast.tree.NE.subtrees.combi1 <- plot_grid(p.Beast.tree.NE.cluster2.subtree, p.Beast.tree.NE.cluster3.subtree, ncol=1, labels=c("C - Cluster 2", "D - Cluster 3"), vjust=1.0, label_size=panel.lab.size, scale=0.95)

p.Beast.tree.NE.subtrees.combi2 <- plot_grid(p.Beast.tree.NE.cluster1.subtree.cluster.highlight.with_clusterID, p.Beast.tree.NE.subtrees.combi1, ncol=2, rel_widths=c(3,2), labels=c("B - Cluster 1", ""), label_size=panel.lab.size)
p.Beast.tree.NE.subtrees.combi2



p.Beast.tree.NE.subtrees.combi3 <- plot_grid(p.Beast.tree.sublineage1.NE.subtree.cluster.highlight, p.Beast.tree.NE.subtrees.combi1, ncol=2, rel_widths=c(8,7), labels=c("B - Sublineage 1 (All)", ""), label_size=panel.lab.size, scale=0.95, vjust=1.0)

p.Beast.tree.NE.subtrees.combi3



Look more closely at population demographics of these clusters

# Metadata on NE cluster 2
PHE.metadata.linked %>% 
  dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster2.subtree@phylo$tip.label) %>%
  dplyr::group_by(Geo_Country, is.NorthEast, gender_orientation) %>%
  dplyr::summarise(Count=n())
`summarise()` has grouped output by 'Geo_Country', 'is.NorthEast'. You can override using the `.groups` argument.
# Metadata on NE cluster 3
PHE.metadata.linked %>% 
  dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label) %>%
  dplyr::group_by(Geo_Country, is.NorthEast, gender_orientation) %>%
  dplyr::summarise(Count=n())
`summarise()` has grouped output by 'Geo_Country', 'is.NorthEast'. You can override using the `.groups` argument.
# Country info on NE cluster 3
TPA.meta2.1 %>% 
  dplyr::filter(Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label) %>%
  dplyr::group_by(Geo_Country) %>%
  dplyr::summarise(Count=n())

# Separate metadata records show Hungarian sample "TPA_HUN180001" came from a male bisexual (MSWM).


Examine SNP scaled tree for distances


# Extract information about SNP distances
TPA.NEcluster3.pyjartree.mrca <- getMRCA(TPA.pyjar.tree, as.character(unlist(TPA.meta2.1[TPA.meta2.1$Sample_Name %in% Beast.tree.NE.cluster3.subtree@phylo$tip.label,"Sample_Name"])))


TPA.NEcluster3.pyjartree.subtree <- tree_subset(TPA.pyjar.tree, node=TPA.NEcluster3.pyjartree.mrca, levels_back=1)

ggtree(TPA.NEcluster3.pyjartree.subtree) + geom_tiplab(size=theme.text.size.within)

ggtree(TPA.NEcluster3.pyjartree.subtree)$data



Do some analysis of nearest neighbour and distances to MRCAs

calculate.years.from.mrca <- function(current.ggtree.phylo, current.ggtree.data){
  #current.ggtree <- Beast.tree.NE.cluster3.subtree
  all.tips <- current.ggtree.phylo$tip.label
  dist.2.mrca <- NULL
  ### put dates into df
  current.ggtree.data$mrca.median <- 2019.5 - current.ggtree.data$height_median
  current.ggtree.data$year <- as.numeric(round(2019.5 - current.ggtree.data$height_median,3))
  current.ggtree.data$mrca.95high <- round(2019.5 - sapply(1:nrow(current.ggtree.data),function(x) as.numeric(unlist(current.ggtree.data[x,"height_0.95_HPD"]))[1]), 3)
  current.ggtree.data$mrca.95low <- round(2019.5 - sapply(1:nrow(current.ggtree.data),function(x) as.numeric(unlist(current.ggtree.data[x,"height_0.95_HPD"]))[2]), 3)
  # extract dates between sample and its MRCA using loop
  for (current.node in all.tips) {
    current.parent <- c(match(current.node,current.ggtree.phylo$tip.label), phangorn::Ancestors(current.ggtree.phylo, match(c(current.node), current.ggtree.phylo$tip.label), "parent"))
    
    current.nodelist <- current.ggtree.data[current.ggtree.data$node %in% current.parent,]
    current.dist.2.mrca <- c(current.node, as.numeric(current.nodelist[1,"year"]-current.nodelist[2,"year"]))
    dist.2.mrca <- rbind(dist.2.mrca, current.dist.2.mrca)
  }
  dist.2.mrca <- data.frame(Sample_Name=as.character(dist.2.mrca[,1]), dist.to.mrca=as.numeric(dist.2.mrca[,2]), stringsAsFactors=F)
  return(dist.2.mrca)
}

### All samples in global tree
dist.mrca.all.TPA <- calculate.years.from.mrca(full.beast2.tree@phylo, full.beast2.tree@data)


Merge dist2MRCA with metadata

PHE.metadata.linked.dist2mrca <- left_join(PHE.metadata.linked, dist.mrca.all.TPA, by="Sample_Name")

p.time2mrca.orientation <- ggplot(PHE.metadata.linked.dist2mrca, aes(gender_orientation, dist.to.mrca, color=gender_orientation)) + 
  geom_quasirandom(size=0.75, alpha=0.5) +
  theme_light() + theme.text.size +
  coord_flip() +
  labs(x="Gender Orientation", y="Years to MRCA", color="Gender Orientation") +
  theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
  scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation)

p.time2mrca.phe_region <- ggplot(PHE.metadata.linked.dist2mrca, aes(phe_centre, dist.to.mrca, color=phe_centre)) + 
  geom_quasirandom(size=0.75, alpha=0.5) +
  theme_light() + theme.text.size +
  coord_flip(ylim=c(0,40)) +
  labs(x="UKHSA Region", y="Years to MRCA", color="UKHSA Region") +
  theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
  scale_color_manual(name="UKHSA\nRegion", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region)

p.time2mrca.phe_region.orientation <- ggplot(PHE.metadata.linked.dist2mrca, aes(phe_centre, dist.to.mrca, color=gender_orientation)) + 
  geom_quasirandom(size=0.75, alpha=0.5) +
  theme_light() + theme.text.size +
  coord_flip(ylim=c(0,20)) +
  labs(x="UKHSA Region", y="Years to MRCA") +
  theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
  scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation)
p.time2mrca.phe_region.orientation
Warning: Removed 15 rows containing missing values (position_quasirandom).

p.time2mrca.sublineage <- ggplot(PHE.metadata.linked.dist2mrca, aes(TPA.pinecone.sublineage, dist.to.mrca, color=TPA.pinecone.sublineage)) + 
  geom_quasirandom(size=0.75, alpha=0.5) +
  theme_light() + theme.text.size +
  coord_flip() +
  labs(x="TPA Lineage", y="Years to MRCA", color="TPA Lineage") +
  theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
  scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)
p.time2mrca.sublineage
Warning: Removed 15 rows containing missing values (position_quasirandom).

p.time2mrca.Lineage <- ggplot(PHE.metadata.linked.dist2mrca, aes(TPA_Lineage, dist.to.mrca, color=TPA_Lineage)) + 
  geom_quasirandom(size=0.75, alpha=0.5) +
  theme_light() + theme.text.size +
  coord_flip() +
  labs(x="TPA Lineage", y="Years to MRCA (Median of Posterior)", color="TPA Lineage") +
  theme(legend.position='bottom', legend.key.size = unit(0.55,"line")) +
  scale_color_manual(values=TPA_Lineage.cols$Lineage.col, breaks=TPA_Lineage.cols$Lineage)



Maybe can make an MST of the North East samples for grapetree?

TPA.pyjar.tree.subset.NorthEast <- ape::keep.tip(TPA.pyjar.tree, as.character(unlist(PHE.metadata.linked[PHE.metadata.linked$phe_centre=="North East","Sample_Name"])))

ggtree(TPA.pyjar.tree.subset.NorthEast)

#write.tree(TPA.pyjar.tree.subset.NorthEast, paste0(Data_input_directory,"TPA.UK-only-NorthEast.pyjar.2022-02-26.tre"))

# Write out a metadata sheet for the relevant information
PHE.metadata.linked.grapetree <- PHE.metadata.linked[,c("Sample_Name", "year","gender_orientation","phe_centre","hivpos","ukborn","TPA_Lineage","TPA.pinecone.sublineage")]
colnames(PHE.metadata.linked.grapetree)[1] <- "ID"

#write.table(PHE.metadata.linked.grapetree, paste0(Data_input_directory,"TPA.UK-only.grapetree.meta.2022-02-03.tsv"), sep = "\t", quote=F, row.names = F)

Alternative approach using MST instead of networks for North East data

# Read in MST
#TPA.NorthEastEngland.Grapetree.file <- paste0(Data_input_directory,"TPA-UK-NorthEast-2022-02-26.GenderOrientation-MSTree.inkscaped.+node-counts+GBMSM.svg")

p.TPA.NorthEastEngland.Grapetree <- ggdraw() + draw_image(TPA.NorthEastEngland.Grapetree.file)
p.TPA.NorthEastEngland.Grapetree


p.TPA.NorthEastEngland.Grapetree.header <- plot_grid(p.TPA.NorthEastEngland.Grapetree, labels=c("A - Network Clusters (North East England)"), label_size=panel.lab.size, scale=0.95)


Plot with beast trees

#p.PHE.NorthEast_MST.with.beast.subtrees.combi <- plot_grid(p.TPA.NorthEastEngland.Grapetree, p.Beast.tree.NE.subtrees.combi3, ncol=1, rel_heights=c(3,6), labels=c("A - Network Clusters (North East England)", ""), label_size=panel.lab.size, scale = 0.95)

p.PHE.NorthEast_MST.with.beast.subtrees.combi <- plot_grid(p.TPA.NorthEastEngland.Grapetree.header, p.Beast.tree.NE.subtrees.combi3, ncol=1, rel_heights=c(3,7))



p.PHE.NorthEast_MST.with.beast.subtrees.combi

#ggsave(paste0(Figure_output_directory,"Fig3_Sublin1.NorthEast.MST+Beast.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=200, height=245, device='pdf', dpi=1200)


Do some analysis of major sublineages over time by region - could this influence observations about sublineages?

# Generate some stats by PHE Region
PHE.major.sublineage.PHEcentre.date <- PHE.metadata.linked %>% 
  dplyr::filter(TPA.pinecone.sublineage %in% c(1,14)) %>%
  dplyr::group_by(TPA.pinecone.sublineage, phe_centre, year) %>%
  dplyr::summarise(Count=n()) %>%
  dplyr::mutate(total.sublin=sum(Count)) %>%
  dplyr::arrange(desc(phe_centre), .by_group=T) %>%
  dplyr::mutate(fraction=Count/total.sublin, cum_fract=cumsum(fraction), cum_fract.mid = cum_fract-(fraction/2))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'phe_centre'. You can override using the `.groups` argument.
ggplot(PHE.major.sublineage.PHEcentre.date, aes(year, phe_centre, size=Count, color=TPA.pinecone.sublineage)) +
  geom_point() + 
  facet_grid(.~TPA.pinecone.sublineage) +
  theme_light() +
  theme.text.size +
  scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage)



p.PHE.major.sublineage.PHEcentre.date.bubbleplot <- ggplot(PHE.major.sublineage.PHEcentre.date, aes(year, TPA.pinecone.sublineage, color=TPA.pinecone.sublineage)) +
  geom_point(alpha=0.65, aes(size=Count)) + 
  geom_line(alpha=0.25) +
  facet_grid(factor(gsub("\\ ","\n",phe_centre), levels=gsub("\\ ","\n",PHE.region.cols.brew$UKHSA.region))~., switch='y') +
  theme_light() +
  theme(strip.placement = "outside") +
  theme(strip.background = element_rect(color='white', fill='white',linetype="solid"), strip.text.y=element_text(color = "grey25",angle=0, size=5)) + 
  scale_size_area(max_size = 4.5,breaks=c(1,5,10,20,30,40)) +
  theme.text.size +
  scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage) + 
  labs(y="Region", x="Year", color="Sublineage") 
 
p.PHE.major.sublineage.PHEcentre.date.bubbleplot
geom_path: Each group consists of only one observation. Do you need to adjust the group aesthetic?


Do some specific analysis for the 3 Northern regions

# Generate some stats by PHE Region
 PHE.metadata.linked %>% 
  dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
  dplyr::summarise(count=n())

 PHE.metadata.linked %>% 
  dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
  dplyr::group_by(year) %>%
  dplyr::summarise(count=n())


p.PHE.major.sublineage.3NorthernRegions <- PHE.metadata.linked %>% 
  dplyr::filter(phe_centre %in% c("North East", "North West", "Yorkshire and Humber")) %>%
  dplyr::group_by(TPA.pinecone.sublineage, year, phe_centre) %>%
  dplyr::summarise(Count=n()) %>%
  ggplot(aes(year, Count, fill=phe_centre)) + 
  geom_bar(stat='identity', width=0.65) +
  scale_fill_manual(values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region) +
  theme_bw() + theme.text.size +
  scale_x_continuous(breaks=seq(2012,2018,1)) +
  scale_y_continuous(breaks=pretty) +
  labs(title="Samples in 3 Northern Regions", x="Collection Year", y="Sample Count", fill="Public Health\nRegion") +
  theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  #geom_text(aes(x=year,y=Count-0.5, label=Count), color='grey95', size=theme.text.size.within) +
  NULL
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'year'. You can override using the `.groups` argument.
p.PHE.major.sublineage.3NorthernRegions


Single linkage network of identical genomes from UK

# Constrain by SNP distance (identical in the asr snp tree)
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta[PHE.alignment.data.dist.melt.meta$Distance.Phylo==0,]

# and a max of 2 years
#PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[PHE.alignment.data.dist.melt.meta.identicals$decimal.date.distance<=2,]


# And make sure that we actually have genetic distance data for all samples within the network
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[!is.na(PHE.alignment.data.dist.melt.meta.identicals$Distance.Phylo),]

# remove self-samples
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[PHE.alignment.data.dist.melt.meta.identicals$same.sample=="different",]


# cleanup some data noise
PHE.alignment.data.dist.melt.meta.identicals <- PHE.alignment.data.dist.melt.meta.identicals[!is.na(PHE.alignment.data.dist.melt.meta.identicals$year.t1),]

# prepare intput data (with edge info)
PHE.alignment.data.dist.melt.meta.identicals.input1 <- PHE.alignment.data.dist.melt.meta.identicals[,c("Taxa1","Taxa2","Distance.Phylo","decimal.date.distance","year.distance","Orientation.Class","epi.time.distance.cat.years","epi.time.distance.cat")]

############
# some issues with update to R4 - double sided matrix
PHE.alignment.data.dist.melt.meta.identicals.input1$edgename <- sapply(1:nrow(PHE.alignment.data.dist.melt.meta.identicals.input1), function(x) paste0(sort(as.character(unlist(PHE.alignment.data.dist.melt.meta.identicals.input1[x,c("Taxa1","Taxa2")]))),collapse="___"))
PHE.alignment.data.dist.melt.meta.identicals.input1 <- PHE.alignment.data.dist.melt.meta.identicals.input1[!duplicated(PHE.alignment.data.dist.melt.meta.identicals.input1$edgename),]

# Also having an issue with taxa as factors here
PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa1 <- as.character(PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa1)
PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa2 <- as.character(PHE.alignment.data.dist.melt.meta.identicals.input1$Taxa2)
############
# Deduplicate

#inverse weight
PHE.alignment.data.dist.melt.meta.identicals.input1$decimal.date.distance.inv <- 1/1/(PHE.alignment.data.dist.melt.meta.identicals.input1$decimal.date.distance+0.04)

# Make actual network
set.seed(1236)
PHE.identicals.network <- network(PHE.alignment.data.dist.melt.meta.identicals.input1, matrix.type = "edgelist", ignore.eval = FALSE, directed = F, loops = F)

#PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "kamadakawai", weights = "decimal.date.distance.inv")
#PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "fruchtermanreingold", weights = "decimal.date.distance")
PHE.identicals.network.gg <- ggnetwork(PHE.identicals.network, layout = "fruchtermanreingold")

PHE.identicals.network.gg$Taxa1 <- PHE.identicals.network.gg$vertex.names

# extract temporal clusters from network
PHE.identicals.network.ig <- asIgraph(PHE.identicals.network)
PHE.identicals.network.components <- data.frame(Taxa1=network.vertex.names(PHE.identicals.network), vertex.no=as.vector(V(PHE.identicals.network.ig)), cluster=igraph::components(PHE.identicals.network.ig)$membership)
PHE.identicals.network.components$Cluster <- paste0("Cluster",PHE.identicals.network.components$cluster)

# merge metadata back in
PHE.identicals.network.gg <- plyr::join(PHE.identicals.network.gg, data.frame(Taxa1=PHE.metadata.linked$Sample_Name, PHE.metadata.linked[,c("phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], stringsAsFactors = F),by="Taxa1", type="left")

PHE.identicals.network.gg <- plyr::join(PHE.identicals.network.gg, data.frame(Taxa1=PHE.identicals.network.components$Taxa1, Cluster=PHE.identicals.network.components$Cluster), by="Taxa1", type="left")


# 
# Add temporal colour scale
#unique(PHE.identicals.network.gg$epi.time.distance.cat)

epi.time.distance.cat.cols <- rev(colorRampPalette(brewer.pal(8, "Greys"))(length(unique(PHE.identicals.network.gg$epi.time.distance.cat))-1))


# Plot network
p.PHE.identicals.network.0SNP <- ggplot(PHE.identicals.network.gg, aes(x = x, y = y, xend = xend, yend = yend)) + 
  geom_edges(alpha=0.90, curvature = 0.2, aes(color=factor(epi.time.distance.cat), linetype=factor(epi.time.distance.cat))) +
  #scale_color_manual(values=c("grey5","grey35","grey55", "grey65", "grey75"), name="SNP\nDistance") +
  scale_color_manual(name="Temporal\nDistance", values = epi.time.distance.cat.cols) +
  scale_linetype(name="Temporal\nDistance") +
  theme_blank() +
  ggnewscale::new_scale_color() + ggnewscale::new_scale("size") +
  #geom_nodelabel(aes(color=gender_orientation, label=paste(Taxa1,year,sep="\n"),fontface = "bold"), alpha=0.8, size=theme.text.size.within-0.4, label.size=0.15, label.padding = unit(0.05, "lines")) +
  geom_nodes(size=2.5, aes(color=gender_orientation), alpha=0.9) + 
  scale_color_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation) + 
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  NULL
p.PHE.identicals.network.0SNP

Plot this against a UK tree?

gheatmap(ggtree(TPA.pyjar.tree.subset.uk),
data.frame(row.names=PHE.identicals.network.components$Taxa1, Cluster=PHE.identicals.network.components$Cluster))


Some stats from this

p.PHE.identical.Orientation_class.bydatedist <- PHE.alignment.data.dist.melt.meta %>%
  dplyr::filter(same.sample=="different", Distance.Phylo==0) %>%
  #filter(decimal.date.distance<=1) %>%
  dplyr::group_by(epi.time.distance.cat, Orientation.Class) %>% 
  dplyr::summarise(Count.class.date=n()) %>%
  dplyr::mutate(sum.class=sum(Count.class.date), fract.class=Count.class.date/sum.class) %>%
  ggplot(aes(x=epi.time.distance.cat, y=Count.class.date, fill=Orientation.Class)) +
  geom_bar(stat='identity', position='stack') +
  theme_bw() +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  labs(x="Time between samples", y="Interaction Count", fill="Orientation Type")
`summarise()` has grouped output by 'epi.time.distance.cat'. You can override using the `.groups` argument.
p.PHE.identical.Orientation_class.bydatedist



  
p.PHE.identical.Orientation_class.byZerodist.cluster <- PHE.identicals.network.gg %>%
  dplyr::filter(!is.na(Orientation.Class)) %>%
  dplyr::group_by(Cluster, Orientation.Class) %>% 
  dplyr::summarise(Count.class.cluster=n()) %>%
  dplyr::mutate(sum.class=sum(Count.class.cluster), fract.class=Count.class.cluster/sum.class) %>%
  dplyr::arrange(desc(sum.class)) %>%
  dplyr::ungroup() %>%
  dplyr::mutate(Cluster=as_factor(Cluster)) %>%
  ggplot(aes(x=Cluster, y=Count.class.cluster, fill=Orientation.Class)) +
  geom_bar(stat='identity', position='stack') + 
  theme_bw() +
  x.theme.axis.rotate +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  labs(x="Identical Genome Cluster", y="Interaction Count", fill="Orientation Type")
`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.Orientation_class.byZerodist.cluster


d.PHE.identical.GenderOrientation.byZerodist.cluster <- left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name")) %>%
  dplyr::group_by(TPA.pinecone.sublineage, Cluster, gender_orientation) %>%
  dplyr::summarise(count.orient.cluster=n()) %>%
  dplyr::mutate(count.cluster=sum(count.orient.cluster), fract=count.orient.cluster/count.cluster) %>%
  dplyr::ungroup() %>%
  dplyr::arrange(desc(count.cluster)) %>%
  dplyr::mutate(Cluster.o=as_factor(Cluster))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'Cluster'. You can override using the `.groups` argument.
# Plot sample counts by genome cluster (coloured by orientation)
p.PHE.identical.GenderOrientation.byZerodist.cluster <- d.PHE.identical.GenderOrientation.byZerodist.cluster %>%
  ggplot(aes(Cluster.o, count.orient.cluster, fill=gender_orientation)) + 
  geom_bar(stat="identity", width=0.65) +
  scale_fill_manual(name="Gender\nOrientation", values=PHE.orientation.cols$orientation.cols, breaks=PHE.orientation.cols$orientation, guide = guide_legend(order = 1)) +
  theme_light() +
  x.theme.axis.rotate + 
  scale_y_continuous(breaks=seq(0,45,5)) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  labs(x="Identical Genome Cluster", y="Sample Count", fill="Patient Gender Orientation") 

# Add details of sublineage  
p.PHE.identical.GenderOrientation.byZerodist.cluster <- p.PHE.identical.GenderOrientation.byZerodist.cluster + 
  ggnewscale::new_scale_color() +
  geom_point(data=(d.PHE.identical.GenderOrientation.byZerodist.cluster %>% select(Cluster.o, TPA.pinecone.sublineage) %>% distinct()), aes(Cluster.o, -1.5, color=TPA.pinecone.sublineage), inherit.aes = F) + scale_color_manual(values=sublineages.cols.brew$sublineage.cols, breaks=sublineages.cols.brew$sublineage, name="Sublineage", guide = guide_legend(order = 2)) +
  NULL

# Add a sublineage axis label (bit of a hack)
p.PHE.identical.GenderOrientation.byZerodist.cluster <- p.PHE.identical.GenderOrientation.byZerodist.cluster + 
  geom_text(data=data.frame(lab="Sublineage", y=-1.5, x=28, stringsAsFactors=F), aes(label=lab, x=x, y=y), hjust = 0.1, size=theme.text.size.within, inherit.aes = F) +
  coord_cartesian(x=c(1, 27), clip='off')
  
p.PHE.identical.GenderOrientation.byZerodist.cluster


#ggsave(paste0(Figure_output_directory,"SupFig6_Identical-SNP-clust_orientation.",format(Sys.Date(),"%Y%m%d"),".pdf"), units='mm', width=120, height=100, device='pdf', dpi=1200)


Possible to introduce some more info into that plot?

d.PHE.identical.region.byZerodist.cluster <- left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name")) %>%
  dplyr::group_by(TPA.pinecone.sublineage, Cluster, phe_centre) %>%
  dplyr::summarise(count.region.cluster=n()) %>%
  dplyr::mutate(count.cluster=sum(count.region.cluster), fract=count.region.cluster/count.cluster) %>%
  dplyr::ungroup() %>%
  dplyr::arrange(desc(count.cluster)) %>%
  dplyr::mutate(Cluster.o=as_factor(Cluster))
`summarise()` has grouped output by 'TPA.pinecone.sublineage', 'Cluster'. You can override using the `.groups` argument.
p.PHE.identical.Region.byZerodist.cluster <- d.PHE.identical.region.byZerodist.cluster %>%
  ggplot(aes(Cluster.o, count.region.cluster, fill=phe_centre)) + 
  geom_bar(stat="identity", width=0.65, position='fill') +
  scale_fill_manual(name="UKHSA\nRegion", values=PHE.region.cols.brew$region.col, breaks=PHE.region.cols.brew$UKHSA.region, guide = guide_legend(order = 1)) +
  theme_light() +
  x.theme.axis.rotate + 
  scale_y_continuous(breaks=seq(0,45,5)) +
  theme.text.size + theme(legend.key.size = unit(0.55,"line"),legend.position='right') +
  guides(fill=guide_legend(ncol=2)) +
  labs(x="Identical Genome Cluster", y="Region Proportion", fill="UKHSA Region") 
p.PHE.identical.byZerodist.cluster.barcombi <- plot_grid(p.PHE.identical.GenderOrientation.byZerodist.cluster + x.theme.strip, p.PHE.identical.Region.byZerodist.cluster, ncol=1, axis="rlt", align=T, rel_heights = c(2,1), labels=c("B","C"), label_size=panel.lab.size)

#p.PHE.identical.byZerodist.cluster.barcombi

#p.PHE.identicals.network.0SNP

plot_grid(p.PHE.identicals.network.0SNP, p.PHE.identical.byZerodist.cluster.barcombi, ncol=1, rel_heights=c(2,3), labels=c("A",""), label_size=panel.lab.size)

PHE.identicals.network.gg.region.scatterpie.groups <- PHE.identicals.network.gg %>%
  dplyr::select(Cluster, Taxa1, phe_centre) %>%
  dplyr::distinct() %>%
  dplyr::group_by(Cluster, phe_centre) %>% 
  dplyr::summarise(Count.centre=n()) %>%
  dplyr::mutate(x=Cluster, y=3.5) %>%
  pivot_wider(names_from="phe_centre", values_from="Count.centre", values_fill=0) %>%
  dplyr::select(Cluster,x,y,unique(PHE.identicals.network.gg$phe_centre)) %>%
  dplyr::ungroup() %>%
  dplyr::mutate(Cluster.numeric=as.numeric(1:27))
`summarise()` has grouped output by 'Cluster'. You can override using the `.groups` argument.
  

p.PHE.identical.GenderOrientation.byZerodist.cluster + 
  ggnewscale::new_scale_fill() #+

NA
NA


Get a few more stats on the largest cluster (Cluster 8)

#d.PHE.identical.GenderOrientation.byZerodist.cluster %>% filter(Cluster=="Cluster8")

PHE.identicals.network.gg.identical.cluster8 <- PHE.identicals.network.gg %>% filter(Cluster=="Cluster8")  %>%
  select(vertex.names, Orientation.Class, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos, Cluster)

sort(unique(PHE.identicals.network.gg.identical.cluster8$year))
[1] 2012 2013 2014 2015 2016 2017 2018


Get some more information about the heterosexual only clusters

PHE.identicals.network.gg.identical_heteroclusters <- PHE.identicals.network.gg %>% filter(Cluster %in% c("Cluster12", "Cluster20", "Cluster27"))  %>%
  select(vertex.names, Cluster, gender_orientation, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos) %>% 
  distinct() %>%
  arrange(Cluster, year, gender_orientation)

PHE.identicals.network.gg.identical_heteroclusters

  And do the same for the small mixed/GBMSM clusters

PHE.identicals.network.gg.identical_not.heteroclusters <- PHE.identicals.network.gg %>% filter(Cluster %notin% c("Cluster12", "Cluster20", "Cluster27", "Cluster8"))  %>%
  select(vertex.names, Cluster, gender_orientation, phe_centre, year, TPA_Lineage, TPA.pinecone.sublineage, hivpos) %>% 
  distinct() %>%
  arrange(Cluster, year, gender_orientation)
PHE.identicals.network.gg.identical_not.heteroclusters

What proportion of heterosexuals have an identical GBMSM paired genome?


# Delineate heterosexual clusters
d.PHE.identical.heterosexual.clusters <- d.PHE.identical.GenderOrientation.byZerodist.cluster %>% 
  dplyr::mutate(is.heterosexual=ifelse(gender_orientation%in% c("MSW", "WSM"), "heterosexual", ifelse(gender_orientation=="GBMSM","GBMSM", "Unknown"))) %>%
  dplyr::group_by(Cluster,is.heterosexual) %>% 
  dplyr::mutate(count.hetero=sum(count.orient.cluster), fract.hetero=sum(count.orient.cluster)/count.cluster) %>%
  dplyr::ungroup() %>%
  dplyr::filter(is.heterosexual=="heterosexual") %>% 
  dplyr::select(-c(count.orient.cluster, gender_orientation, fract)) %>%
  dplyr::distinct() %>%
  dplyr::mutate(cluster.type=ifelse(fract.hetero==1, "hetero.only", "other"))

d.PHE.identical.heterosexual.clusters 

# What proportion of heterosexuals (n=20) are in a heterosexual-only cluster?
d.PHE.identical.heterosexual.clusters %>% 
  dplyr::group_by(cluster.type) %>%
  dplyr::summarise(count.in.hetero.cluster=sum(count.hetero)) %>% 
  dplyr::mutate(fract.in.hetero=count.in.hetero.cluster/sum(count.in.hetero.cluster))
  

#left_join(PHE.identicals.network.components[,c("Taxa1","Cluster")], PHE.metadata.linked[,c("Sample_Name","phe_centre","london","year","age_group","ukborn","gender_orientation","hivpos","TPA.pinecone.sublineage","TPA_Lineage")], by=c("Taxa1"="Sample_Name"))
LS0tCnRpdGxlOiAiUiBOb3RlYm9vayAtIFRyZXBvbmVtYSBVSyBVS0hTQS1jb2hvcnQgQW5hbHlzaXMgMjAyMiIKb3V0cHV0OgogIHBkZl9kb2N1bWVudDogZGVmYXVsdAogIGh0bWxfbm90ZWJvb2s6IGRlZmF1bHQKICB3b3JkX2RvY3VtZW50OiBkZWZhdWx0Ci0tLQoKTWFrZSBhIGNsZWFuIGVudmlyb25tZW50CmBgYHtyfQogcm0obGlzdD1scygpKQpgYGAKXApMb2FkIHBhY2thZ2VzCmBgYHtyfQpwYWNrYWdlcy5saXN0IDwtIGMoImdncGxvdDIiLCJ0cmVlaW8iLCJnZ3RyZWUiLCJnZ25ld3NjYWxlIiwiYXBlIiwiZHBseXIiLCJ0aWR5dmVyc2UiLCJ0aWR5ciIsInBoeXRvb2xzIiwiUkNvbG9yQnJld2VyIiwibHVicmlkYXRlIiwicmVhZHhsIiwiZ2dmb3JjZSIsImdnc3RhbmNlIiwiZ2dyaWRnZXMiLCJjb3dwbG90IiwiaGV4YmluIiwic2NhbGVzIiwiaGF2ZW4iLCJuZXR3b3JrIiwiZ2duZXR3b3JrIiwiaW50ZXJncmFwaCIsImlncmFwaCIsImdncmFwaCIsImdyYXBobGF5b3V0cyIsInNjYXR0ZXJwaWUiLCJtYXBzIiwibWFwZGF0YSIsIm1hcHRvb2xzIiwicmdkYWwiLCJyZ2VvcyIsImJyb29tIiwiZ2dyZXBlbCIsImdncmlkZ2VzIiwibWFnaWNrIiwiZ2diZWVzd2FybSIpCgojInBseXIiLCJDYWlybyIsImdnbWFwIiwiZW1vamlmb250IiwiclBpbmVjb25lIiwicGFpcnNucCIsIkNvb3JkaW5hdGVDbGVhbmVyIiwiZ3JpZEV4dHJhIiwiZGVuZGV4dGVuZCIsImdnZGVuZHJvIiwKCiNCaW9jTWFuYWdlcjo6aW5zdGFsbCgiZ2d0cmVlIikKI0Jpb2NNYW5hZ2VyOjppbnN0YWxsKCJ0cmVlaW8iKQoKZm9yKHBrZyBpbiBwYWNrYWdlcy5saXN0KXsKICBldmFsKGJxdW90ZShsaWJyYXJ5KC4ocGtnKSkpKSB9CmBgYApcCkNvbmZpcm0gY3VycmVudCBlbnZpcm9ubWVudGFsIHNldHVwCmBgYHtyfQpSLlZlcnNpb24oKQpwcmludChzZXNzaW9uSW5mbygpKQpgYGAKXApNYWtlIHNvbWUgc2hvcnRjdXRzIGZvciBwbG90dGluZyAKYGBge3J9CnkudGhlbWUuc3RyaXAgPC0gdGhlbWUoYXhpcy50aXRsZS55ID0gZWxlbWVudF9ibGFuaygpLCBheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy55PSBlbGVtZW50X2JsYW5rKCkpCnkudGhlbWUuc3RyaXAucGFydGlhbCA8LSB0aGVtZShheGlzLnRleHQueSA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50aWNrcy55PSBlbGVtZW50X2JsYW5rKCkpCgp4LnRoZW1lLnN0cmlwIDwtIHRoZW1lKGF4aXMudGl0bGUueCA9IGVsZW1lbnRfYmxhbmsoKSwgYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD0gZWxlbWVudF9ibGFuaygpKQp4LnRoZW1lLnN0cmlwLnBhcnRpYWwgPC0gdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksIGF4aXMudGlja3MueD0gZWxlbWVudF9ibGFuaygpKQp4LnRoZW1lLnN0cmlwLmxhYnMgPC0gdGhlbWUoYXhpcy50ZXh0LnggPSBlbGVtZW50X2JsYW5rKCksYXhpcy50aXRsZS54ID0gZWxlbWVudF9ibGFuaygpKQoKeC50aGVtZS5heGlzLnJvdGF0ZSA8LSB0aGVtZShheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwLCB2anVzdCA9IDAuNSwgaGp1c3Q9MSkpCgpsZWdlbmQuc3RyaXAgPC0gdGhlbWUobGVnZW5kLnBvc2l0aW9uID0gIm5vbmUiKQoKdGhlbWUudGV4dC5zaXplIDwtIHRoZW1lKHRleHQgPSBlbGVtZW50X3RleHQoc2l6ZSA9IDEwKSkKCiclbm90aW4lJyA8LSBOZWdhdGUoJyVpbiUnKQoKbWF4LmZvbnQuc2l6ZSA8LSA3CmJhc2ljLmZvbnQuc2l6ZSA8LSA2Cm1pbi5mb250LnNpemUgPC0gNS4yNQp0aGVtZS50ZXh0LnNpemUgPC0gdGhlbWUodGV4dCA9IGVsZW1lbnRfdGV4dChzaXplID0gYmFzaWMuZm9udC5zaXplKSkKdGhlbWUudGV4dC5zaXplLndpdGhpbiA8LSAoNS8xNCkqbWluLmZvbnQuc2l6ZQpwYW5lbC5sYWIuc2l6ZSA8LSAxMAoKYGBgClwKU3BlY2lmeSByYXcgZGF0YSAtIGdsb2JhbCBkYXRhc2V0CmBgYHtyfQojRGF0YV9pbnB1dF9kaXJlY3RvcnkgPC0gIi9Vc2Vycy9tYjI5L1BhcGVycy9UcmVwb25lbWFfVUstUEhFLWdlbi1lcGlfMjAyMS9EYXRhLyIKI0RhdGFfaW5wdXRfZGlyZWN0b3J5IDwtICIvVXNlcnMvbWIyOS9QYXBlcnMvVHJlcG9uZW1hX1VLLVBIRS1nZW4tZXBpXzIwMjEvUm5vdGVib29rL1Jub3RlYm9va18wOS0yMDIyL2RhdGEvIgpEYXRhX2lucHV0X2RpcmVjdG9yeSA8LSBwYXN0ZTAoZ2V0d2QoKSwgIi9pbnB1dGRhdGEvIikKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIFRyZWUgZGF0YSAKCiMgTUwgdHJlZSAocmVmaW5lZCBkYXRhc2V0KQpUUEEuTUx0cmVlLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtdWJlci5yZW1hc2tlZC4yMDIwLTExLTEwLmdvb2Rjb3YyNS5ndWJiaW5zLlNOUHMuYWxuLnJlbmFtZWQuZml4LXplcm8tZGlzdC50cmVlZmlsZSIpCgojIFB5amFyIHRyZWUgKHJlZmluZWQgZGF0YXNldCkKVFBBLnB5amFyLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtdWJlci5yZW1hc2tlZC4yMDIwLTExLTEwLmdvb2Rjb3YyNS5ndWJiaW5zLlNOUHMuYWxuLnJlbmFtZWQucHlqYXIudHJlIikKCiMgRnVsbCBzaXplIEJFQVNUMiBhbmFseXNpcyAtIHByZXZpb3VzbHkgZ2VuZXJhdGVkIGFzIHBhcnQgb2YgQmVhbGUsIDIwMjEuCmZ1bGwuYmVhc3QyLnRyZWUuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS11YmVyX2JlYXN0Ml9zdHJpY3Qtc2t5bGluZS01MDBNXzEwcG9wX2NvbnNlbnN1cy50cmVlIikKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIE1ldGEgZGF0YSAKCiMgU3VwcGxlbWVudCBmcm9tIFRQQS1VYmVyIHBhcGVyIC0gQmVhbGUsIDIwMjEgClRQQS5tZXRhMi5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiU3VwX0RhdGExX0dsb2JhbF9TYW1wbGUtTWV0YWRhdGFfXzA5LTIwMjIueGxzeCIpCgojIEVuZ2xhbmQgc3BlY2lmaWMgbWV0YWRhdGEgY29sbGF0ZWQgYnkgUEhFL1VLSFNBClBIRS5tZXRhZGF0YS5saW5rZWQuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlN1cF9EYXRhMl9UUEEuVUstb25seS5QSEUubWV0YWRhdGEuMjAyMi0wMi0wMi54bHN4IikKCiMgRW5nbGFuZCBzcGVjaWZpYyBtYXBwaW5nIHNoYXBlZmlsZSBkYXRhIHdpdGggUHVibGljIEhlYWx0aCBCb3VuZGFyaWVzCiMgSW1wb3J0ZWQgZGF0YWZpbGUgZnJvbSBodHRwczovL2dlb3BvcnRhbC5zdGF0aXN0aWNzLmdvdi51ay9kYXRhc2V0cy9wdWJsaWMtaGVhbHRoLWVuZ2xhbmQtY2VudHJlcy1kZWNlbWJlci0yMDE2LWZ1bGwtY2xpcHBlZC1ib3VuZGFyaWVzLWluLWVuZ2xhbmQvZXhwbG9yZT9sb2NhdGlvbj01Mi45NTAwMDAlMkMtMi4wMDAwMDAlMkM2Ljg4ClVLLnB1YmxpY2hlYWx0aC5zaGFwZWZpbGUuZGF0YSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlB1YmxpY19IZWFsdGhfRW5nbGFuZF9DZW50cmVzXyhEZWNlbWJlcl8yMDE2KV9Cb3VuZGFyaWVzIikKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIyMjIEV4dGVybmFsbHkgcGxvdHRlZCBmaWd1cmVzIChlLmcuIEdyYXBlVHJlZSkgZm9yIGluY2x1c2lvbiBpbiBtdWx0aXBhbmVsIGZpZ3VyZXMKCiMgRXh0ZXJuYWxseSBwbG90dGVkIGdyYXBldHJlZSBtaW5pbXVtIHNwYW5uaW5nIHRyZWUgZm9yIHdob2xlIG9mIEVuZ2xhbmQgLSBjb2RlIHRvIGV4dHJhY3Qgc3VidHJlZSB0aGF0IHdhcyB1c2VkIHRvIG1ha2UgdGhpcyBpcyBpbmNsdWRlZCBsYXRlciBpbiB0aGlzIFJub3RlYm9vawpUUEEuVUsuR3JhcGV0cmVlLnN1YmxpbmVhZ2VzLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtVUstMjAyMi0wMi0wMy5zdWJsaW5lYWdlLU1TVHJlZS5JbmtzY2FwZWQuc3ZnIikKCiMgRXh0ZXJuYWxseSBwbG90dGVkIGdyYXBldHJlZSBtaW5pbXVtIHNwYW5uaW5nIHRyZWUgZm9yIHdob2xlIG9mIEVuZ2xhbmQgLSAzLXZhcmlhYmxlIHBsb3RzClRQQS5VSy5HcmFwZXRyZWUuM3dheS5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLVVLLTIwMjItMDItMTYuLU1TVHJlZV8zLXdheS1maWd1cmUuSW5zY2FwZWQtMy5zdmciKQoKIyBFeHRlcm5hbGx5IHBsb3R0ZWQgZ3JhcGV0cmVlIG1pbmltdW0gc3Bhbm5pbmcgdHJlZSBmb3Igd2hvbGUgb2YgRW5nbGFuZCAtIEhJViBzdGF0dXMKVFBBLlVLLkdyYXBldHJlZS5ISVYuZmlsZSA8LSBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS1VSy0yMDIyLTAyLTAzLkhJVnN0YXR1cy1NU1RyZWVfaW5rc2NhcGVkLnN2ZyIpCgojIEV4dGVybmFsbHkgcGxvdHRlZCBncmFwZXRyZWUgbWluaW11bSBzcGFubmluZyB0cmVlIGZvciBOb3J0aCBFYXN0IEVuZ2xhbmQgbmV0d29ya3MKVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLmZpbGUgPC0gcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEtVUstTm9ydGhFYXN0LTIwMjItMDItMjYuR2VuZGVyT3JpZW50YXRpb24tTVNUcmVlLmlua3NjYXBlZC4rbm9kZS1jb3VudHMrR0JNU00uc3ZnIikKCgpgYGAKXApTcGVjaWZ5IGRpcmVjdG9yeSB0byBvdXRwdXQgcGxvdHMKYGBge3J9CkZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5IDwtIHBhc3RlMChnZXR3ZCgpLCAiL0ZpZ3VyZXMvIikKCiMiL1VzZXJzL21iMjkvUGFwZXJzL1RyZXBvbmVtYV9VSy1QSEUtZ2VuLWVwaV8yMDIxL0ZpZ3VyZXMvRmlndXJlX0RyYWZ0aW5nL1dvcmtpbmdfRmlndXJlc18wOC0yMDIyLyIKYGBgClwKUmVhZCBpbiB0cmVlcwpgYGB7cn0KVFBBLk1MdHJlZSA8LSBtaWRwb2ludC5yb290KHJlYWQudHJlZShUUEEuTUx0cmVlLmZpbGUpKQpUUEEucHlqYXIudHJlZSA8LSBtaWRwb2ludC5yb290KHJlYWQudHJlZShUUEEucHlqYXIuZmlsZSkpCmBgYApcClJlYWQgaW4gZmluYWwgb3V0cHV0IG1ldGFkYXRhIGZyb20gR2xvYmFsIFViZXIgc3R1ZHkgKEJlYWxlIDIwMjEpCmBgYHtyfQpUUEEubWV0YTIuMSA8LSByZWFkeGw6OnJlYWRfZXhjZWwoVFBBLm1ldGEyLmZpbGUsc2hlZXQ9IlN1cHBsZW1lbnRhcnlfRGF0YTFfU2FtcGxlLU1ldGEiKQpgYGAKXApDcmVhdGUgYSBjb2xvdXIgc2NoZW1lIGZvciBMaW5lYWdlcywgQ291bnRyaWVzIGFuZCBDb250aW5lbnRzIChjb25zaXN0ZW50IHdpdGggQmVhbGUsIDIwMjEpCmBgYHtyfQojIENvbG91cmluZyBmb3IgY291bnRyeQpjb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzIgPC0gdW5pcXVlKFRQQS5tZXRhMi4xWyxjKCJHZW9fQ291bnRyeSIsIkNvbnRpbmVudCIpXSkKY29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyIDwtIGNvbnRpbmVudGFsLmNvdW50cnkuY29scy5icmV3MltvcmRlcihjb250aW5lbnRhbC5jb3VudHJ5LmNvbHMuYnJldzIkQ29udGluZW50LGNvbnRpbmVudGFsLmNvdW50cnkuY29scy5icmV3MiRHZW9fQ291bnRyeSksXQoKY29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJGNvdW50cnkuY29sIDwtIGMoIiNlYzcwMTQiLCIjZmVjNDRmIiwiI2RlMmQyNiIsIiNmYjZhNGEiLCIjYmRiZGJkIiwiIzczNzM3MyIsYnJld2VyLnBhbChuPTgsIlB1cnBsZXMiKVs0OjhdLGJyZXdlci5wYWwobj04LCJCbHVlcyIpWzM6OF0sYnJld2VyLnBhbChuPTUsIkdyZWVucyIpWzM6NV0sIiNjNTFiOGEiLCIjOGM1MTBhIikKCiMgQ29sb3VyaW5nIGZvciBDb250aW5lbnQKY29udGluZW50YWwuY29scy5icmV3MiA8LSBkYXRhLmZyYW1lKENvbnRpbmVudD1zb3J0KHVuaXF1ZShUUEEubWV0YTIuMSRDb250aW5lbnQpKSxzdHJpbmdzQXNGYWN0b3JzPUYpCmNvbnRpbmVudGFsLmNvbHMuYnJldzIkY29udGluZW50LmNvbCA8LSBjKCIjZmVjNDRmIiwiI2RlMmQyNiIsIiNiZGJkYmQiLCIjMjE3MWI1IiwiIzc0YzQ3NiIsIiNjNTFiOGEiLCIjZWM3MDE0IikKCgojIENvbG91cmluZyBmb3IgVFBBIExpbmVhZ2UKVFBBX0xpbmVhZ2UuY29scyA8LSBkYXRhLmZyYW1lKExpbmVhZ2U9c29ydCh1bmlxdWUoVFBBLm1ldGEyLjEkVFBBX0xpbmVhZ2UpKSxzdHJpbmdzQXNGYWN0b3JzPUYpClRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZS5jb2wgPC0gYygicm95YWxibHVlMiIsICJpbmRpYW5yZWQxIikKI2MoIiM0MzZlZWUiLCAiIzY2NjY2NiIsIiNmZjZhNmEiKQpUUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UgPC0gZmFjdG9yKFRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZSwgbGV2ZWxzPWMoIk5pY2hvbHMiLCJTUzE0Iiwib3V0bGllciIpKQoKIyBMaW5lYWdlIEhleGNvZGVzCiMgcm95YWxibHVlMiAjNDM2ZWVlCiMgaW5kaWFucmVkMSAjZmY2YTZhCmBgYApcCkRlZmluZSBjb2xvdXJzIGZvciBzdWJsaW5lYWdlcwpgYGB7cn0KIyBEZWZpbmUgc3VibGluZWFnZSBjbHVzdGVyaW5nIHNjaGVtZSB1c2luZyBicmV3IGNvbG91cnNjYWxlcwpzdWJsaW5lYWdlcy5jb2xzLmJyZXcgPC0gZGF0YS5mcmFtZSh1bmlxdWUoVFBBLm1ldGEyLjFbLGMoIlRQQV9MaW5lYWdlIiwiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiKV0pLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKc3VibGluZWFnZXMuY29scy5icmV3IDwtIHN1YmxpbmVhZ2VzLmNvbHMuYnJld1tvcmRlcihzdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBX0xpbmVhZ2Usc3VibGluZWFnZXMuY29scy5icmV3JFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSxdCgpzdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluLm9yZGVyIDwtIGFzLm51bWVyaWMoYXMuY2hhcmFjdGVyKHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRUUEEucGluZWNvbmUuc3VibGluZWFnZSkpCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSBzdWJsaW5lYWdlcy5jb2xzLmJyZXdbb3JkZXIoc3VibGluZWFnZXMuY29scy5icmV3JHN1Ymxpbi5vcmRlciksXQoKIyBGb3IgcmV2aXNlZCBib290c3RyYXBwZWQgY2x1c3RlcnMKc3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scyA8LSBjKCIjRkM5MjcyIiwiI0VGM0IyQyIsYnJld2VyLnBhbChuPTQsIkdyZWVucyIpWzI6NF0sYnJld2VyLnBhbChuPTQsIllsT3JCciIpW2MoMiwzKV0sYnJld2VyLnBhbChuPTYsIkJsdWVzIilbMjo2XSxicmV3ZXIucGFsKG49NiwiUHVycGxlcyIpWzI6Nl0sImdyZXk4MCIsImdyZXk4MCIsImdyZXk4MCIsImdyZXk4MCIpCiAgCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSB1bmlxdWUoc3VibGluZWFnZXMuY29scy5icmV3WyxjKCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsInN1YmxpbmVhZ2UuY29scyIpXSkKc3VibGluZWFnZXMuY29scy5icmV3IDwtIHN1YmxpbmVhZ2VzLmNvbHMuYnJld1tvcmRlcihhcy5udW1lcmljKGFzLmNoYXJhY3RlcihzdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSksXQpzdWJsaW5lYWdlcy5jb2xzLmJyZXckVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UgPC0gZmFjdG9yKHN1YmxpbmVhZ2VzLmNvbHMuYnJldyRUUEEucGluZWNvbmUuc3VibGluZWFnZSwgbGV2ZWxzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRUUEEucGluZWNvbmUuc3VibGluZWFnZSkKc3VibGluZWFnZXMuY29scy5icmV3IDwtIHN1YmxpbmVhZ2VzLmNvbHMuYnJld1shaXMubmEoc3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpLF0KCmNvbG5hbWVzKHN1YmxpbmVhZ2VzLmNvbHMuYnJldykgPC0gYygic3VibGluZWFnZSIsInN1YmxpbmVhZ2UuY29scyIpCnN1YmxpbmVhZ2VzLmNvbHMuYnJldyA8LSB1bmlxdWUoc3VibGluZWFnZXMuY29scy5icmV3KQpgYGAKXApSZXN0cmljdCBhbmFseXNpcyB0byBoaWdoIHF1YWxpdHkgZ2Vub21lcyAoYW5kIHRyZWUpCmBgYHtyfQpUUEEubWV0YTIuMSA8LSBUUEEubWV0YTIuMVtUUEEubWV0YTIuMSRmaW5lc2NhbGUuYW5hbHlzaXM9PSJZZXMiLF0KYGBgClwKQ3JlYXRlIGEgIlVLIiB2YXJpYWJsZSwgYW5kIGEgIlBIRSIgdmFyaWFibGUKYGBge3J9ClRQQS5tZXRhMi4xJGlzLlVLIDwtIGlmZWxzZShUUEEubWV0YTIuMSRHZW9fQ291bnRyeT09IlVLIiwiVUsiLCJPdGhlciIpClRQQS5tZXRhMi4xJGlzLlBIRSA8LSBpZmVsc2UoVFBBLm1ldGEyLjEkR2VvX0NvdW50cnk9PSJVSyIgJiBncmVwbCgiUEhFIixUUEEubWV0YTIuMSRTYW1wbGVfTmFtZSksIlBIRSIsIk90aGVyIikKYGBgClwKYGBge3J9CiMgUHJlcGFyZSBNTCB0cmVlClRQQS5NTHRyZWUuZ2d0cmVlIDwtIGdndHJlZShUUEEuTUx0cmVlLGxheW91dCA9ICJmYW4iLG9wZW4uYW5nbGUgPSAxMCwgcmlnaHQ9VCkKCiMgUHJlcGFyZSBjb3VudHJ5IGRhdGFzZXQKVFBBLnJhd3NlcS5jb3VudHJpZXMucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgQ291bnRyeT1UUEEubWV0YTIuMSRHZW9fQ291bnRyeSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgojIFByZXBhcmUgY29udGluZW50IGRhdGFzZXQKVFBBLnJhd3NlcS5jb250aW5lbnRzLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIENvbnRpbmVudD1UUEEubWV0YTIuMSRDb250aW5lbnQsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKIyBQcmVwYXJlIFVLIGRhdGEgc3RyaXAKVFBBLnJhd3NlcS5VSy5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBFbmdsYW5kPVRQQS5tZXRhMi4xJGlzLlVLLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKVFBBLnJhd3NlcS5VSy5wW1RQQS5yYXdzZXEuVUsucCRFbmdsYW5kPT0iVUsiLF0gPC0gIkVuZ2xhbmQiCgojIFByZXBhcmUgUEhFIGRhdGEgc3RyaXAKVFBBLnJhd3NlcS5QSEUucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgUEhFPVRQQS5tZXRhMi4xJGlzLlBIRSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgojIFByZXBhcmUgTWFqb3IgbGluZWFnZSBkYXRhc2V0ClRQQS5yYXdzZXEuTGluZWFnZS5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBMaW5lYWdlPVRQQS5tZXRhMi4xJFRQQV9MaW5lYWdlLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKCiMgUHJlcGFyZSBzdWJsaW5lYWdlIGxpbmVhZ2UgZGF0YXNldApUUEEucmF3c2VxLnN1YkxpbmVhZ2UucCA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1UUEEubWV0YTIuMSRTYW1wbGVfTmFtZSwgU3VibGluZWFnZT1UUEEubWV0YTIuMSRUUEEucGluZWNvbmUuc3VibGluZWFnZSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgoKIyBQcmVwYXJlIFllYXIgZGF0YXNldCAoYWxsIHNhbXBsZXMpClRQQS5yYXdzZXEuYWxsLlllYXJzLnAgPC0gZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFllYXI9VFBBLm1ldGEyLjEkU2FtcGxlX1llYXIsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKCmZsb29yXzV5ZWFycyAgPC0gZnVuY3Rpb24odmFsdWUpeyByZXR1cm4odmFsdWUgLSB2YWx1ZSAlJSA1KSB9ClRQQS5tZXRhMi4xJFNhbXBsZV81eWVhci53aW5kb3cgPC0gcGFzdGUwKGZsb29yXzV5ZWFycyhhcy5udW1lcmljKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyKSksIi0iLGZsb29yXzV5ZWFycyhhcy5udW1lcmljKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyKSkrNSkKIyBTb21lIHNhbXBsZXMgaGF2ZSB1bmNlcnRhaW4gZGF0ZXMgKHVwIHRvIDIwLTMwIHllYXJzIHVuY2VydGFpbnR5KSwgYnV0IGZvciB0aGUgcHVycG9zZXMgb2YgdGhlc2UgcGxvdHRpbmcgY2F0ZWdvcmllcyB3ZSdsbCB1c2UgdGhlIGNlbnRyZXBvaW50IHllYXIKVFBBLm1ldGEyLjEkU2FtcGxlXzV5ZWFyLndpbmRvdyA8LSBzYXBwbHkoMTpucm93KFRQQS5tZXRhMi4xKSwgZnVuY3Rpb24oeCkgaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyW3hdPT0iLSIsTkEsIGlmZWxzZShpcy5uYShUUEEubWV0YTIuMSRTYW1wbGVfNXllYXIud2luZG93W3hdKSxOQSwgaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyW3hdPT0iMTk1MC0xOTgwIiwiMTk2NS0xOTcwIixpZmVsc2UoVFBBLm1ldGEyLjEkU2FtcGxlX1llYXJbeF09PSIxOTYwLTE5ODAiLCIxOTY1LTE5NzAiICxpZmVsc2UoVFBBLm1ldGEyLjEkU2FtcGxlX1llYXJbeF09PSIxOTgwLTE5OTkiLCIxOTg1LTE5OTAiLFRQQS5tZXRhMi4xJFNhbXBsZV81eWVhci53aW5kb3dbeF0pKSkpKSkKCgpUUEEubWV0YTIuMSRTYW1wbGVfeWVhci4xOTkwLmN1dHRvZmYgPC0gaWZlbHNlKFRQQS5tZXRhMi4xJFNhbXBsZV9ZZWFyPjE5OTAsVFBBLm1ldGEyLjEkU2FtcGxlX1llYXIsIjwxOTkwIikKClRQQS5tZXRhMi4xJFNhbXBsZV95ZWFyLjE5OTkuY3V0dG9mZiA8LSBpZmVsc2UoVFBBLm1ldGEyLjEkU2FtcGxlX1llYXI+MTk5OSxUUEEubWV0YTIuMSRTYW1wbGVfWWVhciwiPDE5OTkiKQpUUEEucmF3c2VxLnllYXIuY3V0dG9mZi5wIDwtIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBTYW1wbGUuWWVhcj1UUEEubWV0YTIuMSRTYW1wbGVfeWVhci4xOTk5LmN1dHRvZmYsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKQoKYGBgClwKXAojIEJyaW5nIGluIFBIRSBtZXRhZGF0YQpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZCA8LSByZWFkeGw6OnJlYWRfZXhjZWwoUEhFLm1ldGFkYXRhLmxpbmtlZC5maWxlKQpgYGAKXApEbyBzb21lIGNsZWFudXAgYW5kIGZhY3RvcmluZyBvZiB2YXJpYWJsZXMKYGBge3J9CgpQSEUubWV0YWRhdGEubGlua2VkJGFnZV9ncm91cCA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRhZ2VfZ3JvdXAsIGxldmVscz1yZXYoYygiMTYtMjQiLCIyNS0zNCIsIjM1LTQ0IiwiNDUrIiwiVW5rbm93biIpKSkKClBIRS5tZXRhZGF0YS5saW5rZWQkbG9uZG9uIDwtIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJGxvbmRvbixsZXZlbHM9cmV2KGMoIlllcyIsIk5vIiwiVW5rbm93biIpKSkKUEhFLm1ldGFkYXRhLmxpbmtlZCR1a2Jvcm4gPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkdWtib3JuLGxldmVscz1yZXYoYygiWWVzIiwiTm8iLCJVbmtub3duIikpKQpQSEUubWV0YWRhdGEubGlua2VkJGhpdnBvcyA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRoaXZwb3MsIGxldmVscz1yZXYoYygiWWVzIiwiTm8iLCJVbmtub3duIikpKQoKIyBuZWVkIHRvIHVwZGF0ZSB0ZXJtaW5vbG9neSBvZiAnTVNNJyB0byAnR0JNU00nClBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRnZW5kZXJfb3JpZW50YXRpb249PSJNU00iLCJnZW5kZXJfb3JpZW50YXRpb24iXSA8LSAiR0JNU00iClBIRS5tZXRhZGF0YS5saW5rZWQkZ2VuZGVyX29yaWVudGF0aW9uIDwtIGZhY3RvcihQSEUubWV0YWRhdGEubGlua2VkJGdlbmRlcl9vcmllbnRhdGlvbiwgbGV2ZWxzPXJldihjKCJNU1ciLCJHQk1TTSIsIldTTSIsIk1Vbmtub3duIiwiVW5rbm93biIpKSkKClBIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZSA8LSBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlLCBsZXZlbHM9cmV2KGMoIkVhc3QgTWlkbGFuZHMiLCAiRWFzdCBvZiBFbmdsYW5kIiwgIkxvbmRvbiIsICJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiU291dGggRWFzdCIsICJTb3V0aCBXZXN0IiwgIldlc3QgTWlkbGFuZHMiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiLCAiVUsgKG5vdCBFbmdsYW5kKSIsICJOb3QgS25vd24iKSkpCgpQSEUubWV0YWRhdGEubGlua2VkJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlIDwtICBmYWN0b3IoUEhFLm1ldGFkYXRhLmxpbmtlZCRUUEEucGluZWNvbmUuc3VibGluZWFnZSwgbGV2ZWxzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKQoKYGBgClwKXAojIyMgRXh0cmFjdCBpbmZvcm1hdGlvbiBhYm91dCBkdXBsaWNhdGVzCmBgYHtyfQpQSEUubWV0YWRhdGEuZHVwbGljYXRlcyA8LSBQSEUubWV0YWRhdGEubGlua2VkWyFpcy5uYShQSEUubWV0YWRhdGEubGlua2VkJGR1cF9mbGFnKSxdClBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzIDwtIFBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzWyFpcy5uYShQSEUubWV0YWRhdGEuZHVwbGljYXRlcyRTYW1wbGVfTmFtZSksXQoKClBIRS5wYXRpZW50Lm1hdGNoZXMgPC0gZGF0YS5mcmFtZSgKICAgIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkdXBfZmxhZyA9IGMoIjFBIiwiMUIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiMkEiLCIyQiIsIjNBIiwiM0IiLCI0QSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICI0QiIsIjVBIiwiNUIiKSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBkdXBfUGF0aWVudCA9IGMoIlBhdGllbnQgMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQYXRpZW50IDEiLCJQYXRpZW50IDIiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiUGF0aWVudCAyIiwiUGF0aWVudCAzIiwiUGF0aWVudCAzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIlBhdGllbnQgNCIsIlBhdGllbnQgNCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJQYXRpZW50IDUiLCJQYXRpZW50IDUiKSwKICAgICAgICAgICAgICAgICAgICAgICAgIGR1cF9QYXRpZW50X1NhbXBsZSA9IGMoInNhbXBsZSAxIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZSAyIiwic2FtcGxlIDEiLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAic2FtcGxlIDIiLCJzYW1wbGUgMSIsInNhbXBsZSAyIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgInNhbXBsZSAxIiwic2FtcGxlIDIiLCJzYW1wbGUgMSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJzYW1wbGUgMiIpCiAgICAgICAgICAgICAgICAgICAgICAgKQogICAgICAgICAgICAgICAgICAgICAgIAoKUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMgPC0gbGVmdF9qb2luKFBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzLCBQSEUucGF0aWVudC5tYXRjaGVzLCBieT0iZHVwX2ZsYWciKQoKUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMKYGBgCgpEdXBsaWNhdGUgU2FtcGxlcyBtaXNzaW5nIG1ldGFkYXRhIGFyZSBhbGwgJ25ldyBkdXBsaWNhdGVzJyBhbmQgd2VyZSBleGNsdWRlZCBkdWUgdG8gbG93IG1hcHBpbmcgY292ZXJhZ2UgKGFsbCBjaGVja2VkKS4KXApTYW1wbGVzIGxhYmVsbGVkICdaQScgYW5kICdYQicgaGFkIGR1cGxpY2F0ZXMgaW4gdGhlIG9yaWdpbmFsIGRhdGFzZXQsIGJ1dCB0aGUgcmVjaXByb2NhbCBwYWlycyB3ZXJlIGV4Y2x1ZGVkIGR1ZSB0byBxdWFsaXR5IGlzdWVzLgpcCkF2YWlsYWJsZSBwYWlycyAtIFBhdGllbnQgMywgUGF0aWVudCA0CgpgYGB7cn0KUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMucGFpcmVkIDwtIFBIRS5tZXRhZGF0YS5kdXBsaWNhdGVzW1BIRS5tZXRhZGF0YS5kdXBsaWNhdGVzJGR1cF9QYXRpZW50ICVpbiUgYygiUGF0aWVudCAzIiwiUGF0aWVudCA0IiksXQpQSEUubWV0YWRhdGEuZHVwbGljYXRlcy5wYWlyZWRbb3JkZXIoUEhFLm1ldGFkYXRhLmR1cGxpY2F0ZXMucGFpcmVkJGR1cF9QYXRpZW50LCBQSEUubWV0YWRhdGEuZHVwbGljYXRlcy5wYWlyZWQkeWVhcixQSEUubWV0YWRhdGEuZHVwbGljYXRlcy5wYWlyZWQkbW9udGgpLGMoIlNhbXBsZV9OYW1lIiwiZHVwX1BhdGllbnQiLCAibW9udGguZml4IiwgInllYXIiKV0KYGBgClwKVGhlc2Ugd2lsbCBiZSByZXZpc2l0ZWQgbGF0ZXIgaW4gdGhlIGFuYWx5c2lzLiAKXApQYXRpZW50IDQKSElWLXZlIE1TTSAoNDUrKSwgVUsgYm9ybiwgUEhFIHJlZ2lvbiBECjIgc2FtcGxlcywgY29sbGVjdGVkIGluIHRoZSBzYW1lIG1vbnRoIGFuZCB5ZWFyCkJvdGggc2FtcGxlcyBhcmUgc3VibGluZWFnZSAxLCBhbmQgaWRlbnRpY2FsICgwIHB3U05QcykKTGlrZWx5IHRoZSBzYW1lIGluZmVjdGlvbiAoZGVwZW5kaW5nIG9uIGRhdGVzLCB0cmVhdG1lbnQsIGV0YyksIGJ1dCBjYW7igJl0IHJ1bGUgb3V0IHJlaW5mZWN0aW9uIHdpdGggc2FtZSBzdHJhaW4uClwKUGF0aWVudCAzCkhJVi12ZSBNU00gKDM1LTQ0KSwgbm90IFVLIGJvcm4sIGJhc2VkIGluIExvbmRvbiAoQykKMiBzYW1wbGVzLCBjb2xsZWN0ZWQgOSBtb250aHMgYXBhcnQKQm90aCBzYW1wbGVzIGFyZSBzdWJsaW5lYWdlIDEsIGJ1dCBoYXZlIDcgcGFpcndpc2UgU05QcyBiZXR3ZWVuIHRoZW0gKGxvYWRzISkKUmVpbmZlY3Rpb24g4oCTIHByb2JhYmx5IGZyb20gYSBkaWZmZXJlbnQgdHJhbnNtaXNzaW9uIG5ldHdvcmsKXApcCkhvd2V2ZXIsIGJhc2VkIG9uIHRoZSBzYW1wbGUgZGF0ZXMsIGFzIHdlbGwgYXMgdGhlIG91dGNvbWUgb2YgdGhlIGRvd25zdHJlYW0gZ2VuZXRpYyBhbmFseXNpcywgd2UgY2FuIHNlZSB0aGF0IFBhdGllbnQgMyBoYXMgZHVwbGljYXRlIGluZmVjdGlvbiBldmVudHMgKGRpZmZlcmVudCBkYXRlcywgMTAgbW9udGhzIGFwYXJ0KSBhbmQgdGhlIGdlbm9tZXMgYXJlIGRpc3RpbmN0ICg3IFNOUHMgYXBhcnQpLCB3aGVyZWFzIFBhdGllbnQgNCBzYW1wbGVzIHdlcmUgY29sbGVjdGVkIGluIHRoZSBzYW1lIG1vbnRoIGFuZCB5ZWFyIChpLmUuIGFyZSBsaWtlbHkgZHVwbGljYXRlcyBmcm9tIHRoZSBzYW1lIGluZmVjdGlvbikgYW5kIGhhcyBpZGVudGljYWwgZ2Vub21lcy4KXApGb3IgZG93bnN0cmVhbSBhbmFseXNpcyBwdXJwb3Nlcywgd2Ugd2lsbCByZXRhaW4gYm90aCBzYW1wbGVzIGZvciBQYXRpZW50IDMgKGRpc2NyZXRlIGluZmVjdGlvbnMpLCBidXQgZXhjbHVkZSBvbmUgc2FtcGxlIGZyb20gUGF0aWVudCA0IChkdXBsaWNhdGUgaW5mZWN0aW9uIHNhbXBsZXMpIC0gJ1BIRTE1MDEyNkEnIGhhcyBtdWNoIGJldHRlciBnZW5vbWUgY292ZXJhZ2UsIHNvIGV4Y2x1ZGUgJ1BIRTE1MDEyNUEnClwKXAojIyMgRnVydGhlciBFeGNsdXNpb25zIFwKUEhFMTMwMDU2QSAtIGR1cGxpY2F0ZSBvZiBQSEUxMzAwNTdCIChhbHJlYWR5IHJlbW92ZWQsIHNvIG5vdCByZWxldmFudCkgLSBkb24ndCBleGNsdWRlIQpQSEUxNzA0MDJBIC0gcXVhbGl0eSBjb250cm9sIHNhbXBsZQpQSEUxNzAzNzhBIC0gcXVhbGl0eSBjb250cm9sIHNhbXBsZQoKXApFeGNsdWRlIGR1cGxpY2F0ZSBzZXF1ZW5jZXMKYGBge3J9CmR1cGxpY2F0ZS5leGNsdXNpb24ubGlzdCA8LSBjKCJQSEUxNTAxMjVBIiwiUEhFMTcwNDAyQSIsIlBIRTE3MDM3OEEiKQpQSEUubWV0YWRhdGEubGlua2VkIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRTYW1wbGVfTmFtZSAlbm90aW4lIGR1cGxpY2F0ZS5leGNsdXNpb24ubGlzdCxdCmBgYApcCgojIyMgTW92aW5nIG9uLi4uIFwKCkRlZmluZSBzb21lIGNvbG91ciBzY2hlbWVzCmBgYHtyfQojIGRlZmluZSBzb21lIGNvbG9ycyBmb3IgZWFjaCByZWdpb24KUEhFLnJlZ2lvbi5jb2xzLmJyZXcgPC0gZGF0YS5mcmFtZShVS0hTQS5yZWdpb249YygiTm9ydGggRWFzdCIsICJOb3J0aCBXZXN0IiwgIllvcmtzaGlyZSBhbmQgSHVtYmVyIiwgIkVhc3QgTWlkbGFuZHMiLCAiV2VzdCBNaWRsYW5kcyIsICJFYXN0IG9mIEVuZ2xhbmQiLCAiTG9uZG9uIiwgIlNvdXRoIEVhc3QiLCJTb3V0aCBXZXN0IiwiVUsgKG5vdCBFbmdsYW5kKSIsICJOb3QgS25vd24iKSwgc3RyaW5nc0FzRmFjdG9ycz1GKQpQSEUucmVnaW9uLmNvbHMuYnJldyRyZWdpb24uY29sIDwtIGMoIiNBNkNFRTMiLCIjMUY3OEI0IiwiI0NBQjJENiIsIiMzM0EwMkMiLCIjQjJERjhBIiwiI0ZGN0YwMCIsIiNFMzFBMUMiLCIjRkI5QTk5IiwiI0Q0QkIwMiIsImdyZXk3NSIsImdyZXkyNSIpCgojIEhJViBjb2xvciBzY2hlbWUKUEhFLmhpdi5jb2xzIDwtIGRhdGEuZnJhbWUoaGl2cG9zPXJldihzb3J0KHVuaXF1ZShQSEUubWV0YWRhdGEubGlua2VkJGhpdnBvcykpKSwgc3RyaW5nc0FzRmFjdG9ycz1GKQpQSEUuaGl2LmNvbHMkaGl2LmNvbHMgPC0gYygiIzFmNzhiNCIsIiNiMmRmOGEiLCJncmV5NzUiKQoKIyBPcmllbnRhdGlvbiBjb2xvdXIgc2NoZW1lClBIRS5vcmllbnRhdGlvbi5jb2xzIDwtIGRhdGEuZnJhbWUob3JpZW50YXRpb249cmV2KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkZ2VuZGVyX29yaWVudGF0aW9uKSkpLCBzdHJpbmdzQXNGYWN0b3JzPUYpClBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uIDwtIGZhY3RvcihQSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbiwgbGV2ZWxzPXJldihzb3J0KHVuaXF1ZShQSEUubWV0YWRhdGEubGlua2VkJGdlbmRlcl9vcmllbnRhdGlvbikpKSwgbGFiZWxzPWMoIk1TVyIsIkdCTVNNIiwiV1NNIiwiTVVua25vd24iLCJVbmtub3duIikpClBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLmNvbHMgPC0gYygiIzFmNzhiNCIsIiNiMmRmOGEiLCIjZmI5YTk5IiwiI2E2Y2VlMyIsImdyZXk3NSIpCgojIFVLIGJvcm4gY29sb3VyIHNjaGVtZQpQSEUudWtib3JuLmNvbHMgPC0gZGF0YS5mcmFtZSh1a2Jvcm49cmV2KHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkdWtib3JuKSkpLHVrYm9ybi5jb2xzPWMoIiMxZjc4YjQiLCIjYjJkZjhhIiwiZ3JleTc1Iiksc3RyaW5nc0FzRmFjdG9ycyA9IEYpCgojIExvbmRvbiBiYXNlZCBjb2xvdXIgc2NoZW1lClBIRS5sb25kb24uY29scyA8LSBkYXRhLmZyYW1lKGxvbmRvbj1yZXYoc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCRsb25kb24pKSksbG9uZG9uLmNvbHM9YygiIzFmNzhiNCIsIiNiMmRmOGEiLCJncmV5NzUiKSxzdHJpbmdzQXNGYWN0b3JzID0gRikKCgojIEFnZSBncm91cCBjb2xvdXIgc2NoZW1lClBIRS5BZ2UuY29scyA8LSBkYXRhLmZyYW1lKGFnZV9ncm91cD1yZXYoc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCRhZ2VfZ3JvdXApKSksc3RyaW5nc0FzRmFjdG9ycyA9IFQpClBIRS5BZ2UuY29scyRhZ2VfZ3JvdXAuY29scyA8LSBjKGJyZXdlci5wYWwobj00LCJZbEduQnUiKSwiZ3JleTc1IikKCiMgU2FtcGxlIERhdGUgY29sb3VyIHNjaGVtZQpQSEUueWVhci5jb2xzIDwtIGRhdGEuZnJhbWUoeWVhcj0oc29ydCh1bmlxdWUoUEhFLm1ldGFkYXRhLmxpbmtlZCR5ZWFyKSkpLHN0cmluZ3NBc0ZhY3RvcnMgPSBUKQpQSEUueWVhci5jb2xzJHllYXIuY29scyA8LSBicmV3ZXIucGFsKG49NywiWWxPclJkIikKCiMgU2FtcGxlIERhdGUgKGFsbCBnbG9iYWwgZGF0YSwgYnV0IHdpdGggMTk5MCBjdXR0b2ZmKQpUUEEueWVhci5jdXR0b2ZmLmNvbHMgPC0gZGF0YS5mcmFtZShkYXRlLmN1dHRvZmY9YygiPDE5OTkiLDE5OTk6MjAxOSksIGRhdGUuY3V0dG9mZi5jb2w9YygiI0YyRjJGMiIsY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDcsICJZbE9yUmQiKSkobGVuZ3RoKDE5OTk6MjAxOSkpKSkKCgpgYGAKXApcCiMjIyMjCiMjIEZpcnN0IGRlc2NyaWJlIHRoZSBzZXF1ZW5jZWQgcG9wdWxhdGlvbiBhcyBhIHdob2xlClwKU2V0IG9yZGVyIG9mIFBIRSByZWdpb25zCmBgYHtyfQpQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmUgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZSwgbGV2ZWxzPXJldihQSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pKQpgYGAKXApHZW5lcmF0ZSBzb21lIGJhc2ljIHN0YXRpc3RpY3MgYWJvdXQgZ2VvZ3JhcGhpY2FsIFBIRSByZWdpb25zIChhbm9ueW1pc2VkKQpgYGB7cn0KUEhFLmNvdW50LmFsbCA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5yZWdpb249bigpKQoKUEhFLmNvdW50LnllYXJzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieSh5ZWFyKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci55ZWFyPW4oKSkgJT4lCiAgdW5ncm91cCgpICU+JQogIGRwbHlyOjptdXRhdGUocGVyYy5wZXIueWVhcj0oY291bnQucGVyLnllYXIvc3VtKGNvdW50LnBlci55ZWFyKSkqMTAwKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IEhJViBzdGF0dXMKUEhFLkhJVi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGhpdnBvcykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGhpdnBvcyksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdCA9IGN1bXN1bShmcmFjdGlvbikpICU+JQogIGRwbHlyOjptdXRhdGUoY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JQogIGRwbHlyOjptdXRhdGUoSElWLnBlcmM9KENvdW50L3N1bShDb3VudCkqMTAwKSkKICAKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IGdlbmRlciBvcmllbnRhdGlvbgpQSEUub3JpZW50YXRpb24uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGdlbmRlcl9vcmllbnRhdGlvbiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUgCiAgZHBseXI6Om11dGF0ZShvcmllbnRhdGlvbi5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgVUsgYm9ybiAodmFndWUgY2F0ZWdvcnkgdGhhdCdzIHVuZm9ydHVuYXRlbHkgb25seSBtYXJnaW5hbGx5IGhlbHBmdWwpClBIRS5VS2Jvcm4uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieSh1a2Jvcm4pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKHVrYm9ybiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUgCiAgZHBseXI6Om11dGF0ZShVS2Jvcm4ucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQogIAojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgTG9uZG9uIGJhc2VkClBIRS5Mb25kb24uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShsb25kb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGxvbmRvbiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKExvbmRvbi5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgQWdlIGdyb3VwClBIRS5BZ2UuY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShhZ2VfZ3JvdXApICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGFnZV9ncm91cCksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKEFnZS5wZXJjPShDb3VudC9zdW0oQ291bnQpKjEwMCkpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgTGluZWFnZSBncm91cApQSEUuTGluZWFnZS5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQV9MaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhUUEFfTGluZWFnZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKSAlPiUKICBkcGx5cjo6bXV0YXRlKExpbmVhZ2UucGVyYz0oQ291bnQvc3VtKENvdW50KSoxMDApKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IHN1YmxpbmVhZ2UgZ3JvdXAKUEhFLnN1YmxpbmVhZ2UuY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwucmVnaW9uPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lCiAgZHBseXI6Om11dGF0ZShTdWJsaW5lYWdlLnBlcmM9KENvdW50L3N1bShDb3VudCkqMTAwKSkKYGBgClwKTWFrZSBzb21lIHBsb3RzCmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9OH0KIyBNYWtlIGhiYXIgcGxvdCBvZiBzYW1wbGUgY291bnRzIGJ5IHJlZ2lvbgpwLmFsbC5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmNvdW50LmFsbCwgYWVzKHg9Y291bnQucGVyLnJlZ2lvbix5PSIiKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJzdGFjayIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J25vbmUnKSArCiAgc2NhbGVfZmlsbF9tYW51YWwodmFsdWVzPSJncmV5MzAiKSArIAogIGdlb21fdGV4dChkYXRhPVBIRS5jb3VudC5hbGwsIGFlcygoY291bnQucGVyLnJlZ2lvbisxMiksICIiLGxhYmVsPWNvdW50LnBlci5yZWdpb24pLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIGxhYnMoeT0iQWxsIiwgeD0iU2FtcGxlIENvdW50IikgKwogIGNvb3JkX2NhcnRlc2lhbih4bGltPWMoMCwyNjApKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9NCkpIAojcC5hbGwuaGJhcnBsb3QKCiMgbWFrZSB0ZW1wb3JhbCBidWJibGVwbG90IG9mIGNvdW50cyBieSByZWdpb24KcC5hbGwueWVhci5idWJibGVwbG90IDwtIGdncGxvdChQSEUuY291bnQueWVhcnMsIGFlcyhhcy5udW1lcmljKHllYXIpLCB5PSJBbGwiKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC42NSwgYWVzKHNpemU9Y291bnQucGVyLnllYXIpKSArIAogIGdlb21fbGluZShhbHBoYT0wLjI1KSArCiAgZ3VpZGVzKGNvbG91cj0nbm9uZScpICsKICBzY2FsZV9zaXplX2FyZWEobWF4X3NpemUgPSA3LGJyZWFrcz1jKDEsNSwxMCwyNSw1MCkpICsgCiAgZ3VpZGVzKHNpemU9Z3VpZGVfbGVnZW5kKG5yb3c9MikpICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9ImdyZXkzMCIpICsgCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIGxhYnMoeT0iIiwgeD0iU2FtcGxlIFllYXIiLCBzaXplPSJDb3VudCIpIAojcC5hbGwueWVhci5idWJibGVwbG90CgojIE1ha2UgcHJvcG9ydGlvbmFsIGhiYXIgcGxvdCBvZiBISVYgc3RhdHVzCnAuYWxsLmhpdi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLkhJVi5jb3VudHMsIGFlcyhDb3VudCx5PSIiLGZpbGw9aGl2cG9zKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nbm9uZScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJISVYgK3ZlIix2YWx1ZXM9UEhFLmhpdi5jb2xzJGhpdi5jb2xzLCBicmVha3M9UEhFLmhpdi5jb2xzJGhpdnBvcykgKwogIGxhYnMoeT0iQWxsIiwgeD0iSElWICt2ZSIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5ISVYuY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgeT0iIixsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgTlVMTAojcC5hbGwuaGl2LmhiYXJwbG90CgpwLmFsbC5vcmllbnRhdGlvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLm9yaWVudGF0aW9uLmNvdW50cywgYWVzKENvdW50LHk9IiIsZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik9yaWVudGF0aW9uIix2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uKSArCiAgbGFicyh5PSJBbGwiLCB4PSJPcmllbnRhdGlvbiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCB5PSIiLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLmFsbC5vcmllbnRhdGlvbi5oYmFycGxvdAoKcC5hbGwudWtib3JuLmhiYXJwbG90IDwtIGdncGxvdChQSEUuVUtib3JuLmNvdW50cywgYWVzKENvdW50LHk9IiIsZmlsbD11a2Jvcm4pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLXG5Cb3JuIix2YWx1ZXM9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybi5jb2xzLCBicmVha3M9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybikgKwogIGxhYnMoeT0iQWxsIiwgeD0iVUsgQm9ybiIpICsKICAjZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuVUtib3JuLmNvdW50cywgYWVzKGN1bV9mcmFjdC5taWQsIHk9IiIsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AuYWxsLnVrYm9ybi5oYmFycGxvdAoKcC5hbGwuTG9uZG9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUuTG9uZG9uLmNvdW50cywgYWVzKENvdW50LHk9IiIsZmlsbD1sb25kb24pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdub25lJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkxvbmRvbiIsdmFsdWVzPVBIRS5sb25kb24uY29scyRsb25kb24uY29scywgYnJlYWtzPVBIRS5sb25kb24uY29scyRsb25kb24pICsKICBsYWJzKHk9IkFsbCIsIHg9IkxvbmRvbiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5Mb25kb24uY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgeT0iIixsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5hbGwuTG9uZG9uLmhiYXJwbG90CgpwLmFsbC5BZ2UuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5BZ2UuY291bnRzLCBhZXMoQ291bnQseT0iIixmaWxsPWFnZV9ncm91cCkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J25vbmUnKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iQWdlXG5Hcm91cCIsdmFsdWVzPVBIRS5BZ2UuY29scyRhZ2VfZ3JvdXAuY29scywgYnJlYWtzPVBIRS5BZ2UuY29scyRhZ2VfZ3JvdXApICsKICBsYWJzKHk9IkFsbCIsIHg9IkFnZSBHcm91cCIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5BZ2UuY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgeT0iIixsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5hbGwuQWdlLmhiYXJwbG90CmBgYApcClBsb3QgY29tYmluZWQgcGxvdCBmb3IgJ2FsbCBzYW1wbGVzJwpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEwfQpQSEUuYWxsLmNvbWJpcGxvdC4xIDwtIHBsb3RfZ3JpZChwLmFsbC55ZWFyLmJ1YmJsZXBsb3QsIHAuYWxsLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5hbGwub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLmFsbC5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLmFsbC5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDQsMiwyLDIsMiksIHNjYWxlPTAuOSkKClBIRS5hbGwuY29tYmlwbG90LjEKYGBgClwKXApOZXh0IGp1c3QgZGVzY3JpYmUgcG9wdWxhdGlvbiBkaXN0cmlidXRpb25zIGJ5IFBIRSByZWdpb24KYGBge3J9CiMgZ2VuZXJhdGUgc29tZSBiYXNpYyBzdGF0aXN0aWNzIGFib3V0IGdlb2dyYXBoaWNhbCBQSEUgcmVnaW9ucyAoYW5vbnltaXNlZCkKUEhFLmdlby5jb3VudCA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIucmVnaW9uPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5jb3VudD1zdW0oY291bnQucGVyLnJlZ2lvbiksZnJhY3Rpb249Y291bnQucGVyLnJlZ2lvbi90b3RhbC5jb3VudCkKClBIRS5nZW8uY291bnQueWVhcnMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUseWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5wZXIucmVnaW9uLnllYXI9bigpKQoKUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLHllYXIsVFBBX0xpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLnJlZ2lvbi55ZWFyPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5jb3VudC55ZWFyPXN1bShjb3VudC5wZXIucmVnaW9uLnllYXIpKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tPVRQQV9MaW5lYWdlLCB2YWx1ZXNfZnJvbSA9IGNvdW50LnBlci5yZWdpb24ueWVhcikKUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlW2lzLm5hKFBIRS5nZW8uY291bnQueWVhcnMubGluZWFnZSldIDwtIDAKUEhFLmdlby5jb3VudC55ZWFycy5saW5lYWdlJHllYXIgPC0gYXMubnVtZXJpYyhQSEUuZ2VvLmNvdW50LnllYXJzLmxpbmVhZ2UkeWVhcikKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBISVYgc3RhdHVzClBIRS5nZW8uSElWLmNvdW50cyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSxoaXZwb3MpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLnJlZ2lvbi5oaXY9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oY291bnQucGVyLnJlZ2lvbi5oaXYpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPWNvdW50LnBlci5yZWdpb24uaGl2L3RvdGFsLnJlZ2lvbikgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhoaXZwb3MpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShjdW1fZnJhY3QgPSBjdW1zdW0oZnJhY3Rpb24pKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKIyBEb3VibGUgQ2hlY2sgSElWIHN0YXR1cyBkYXRhIGZvciBub24tUEhFIGRhdGFzZXQgLSBjb25maXJtZWQgbm8gSElWK3ZlcyBmcm9tIG5vbi1NU00uIApQSEUuc291cmNlbGFiLkhJVi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGlzLlBIRSwgZ2VuZGVyX29yaWVudGF0aW9uLCBoaXZwb3MpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQucGVyLm9yaWVudGF0aW9uLmhpdj1uKCkpICMlPiUKICAjZHBseXI6OmZpbHRlcihpcy5QSEUhPSJQSEUiKQoKIyBHZXQgdG90YWwgcG9wdWxhdGlvbiBzdGF0cyBmb3IgSElWClBIRS5hbGwuSElWLmNvdW50cyA8LSAgUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGhpdnBvcykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5oaXY9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKGNvdW50LnRvdGFsPXN1bShjb3VudC5oaXYpLCBmcmFjdGlvbj1jb3VudC5oaXYvY291bnQudG90YWwpCgogIAojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgZ2VuZGVyIG9yaWVudGF0aW9uClBIRS5vcmllbnRhdGlvbi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShvcmllbnRhdGlvbi5jb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUob3JpZW50YXRpb24ucGVyY2VudD0ob3JpZW50YXRpb24uY291bnQvc3VtKG9yaWVudGF0aW9uLmNvdW50KSoxMDApKQoKUEhFLmdlby5vcmllbnRhdGlvbi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsZ2VuZGVyX29yaWVudGF0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb249bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oY291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbikpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoZ2VuZGVyX29yaWVudGF0aW9uKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Y291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbi90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkgJT4lIAogIGRwbHlyOjptdXRhdGUob3JpZW50YXRpb24ucGVyY2VudD0oY291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbi9zdW0oY291bnQucGVyLnJlZ2lvbi5vcmllbnRhdGlvbikqMTAwKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBVSyBib3JuClBIRS5nZW8uVUtib3JuIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLCB1a2Jvcm4pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKHVrYm9ybiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQogIAojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgTG9uZG9uIGJhc2VkClBIRS5nZW8uTG9uZG9uIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShwaGVfY2VudHJlLCBsb25kb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKGxvbmRvbiksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IEFnZSBncm91cApQSEUuZ2VvLkFnZSA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSwgYWdlX2dyb3VwKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhhZ2VfZ3JvdXApLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5yZWdpb24sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBMaW5lYWdlIGdyb3VwClBIRS5nZW8uTGluZWFnZSA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkocGhlX2NlbnRyZSwgVFBBX0xpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnJlZ2lvbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKFRQQV9MaW5lYWdlKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwucmVnaW9uLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgojIEdlbmVyYXRlIHNvbWUgc3RhdHMgYWJvdXQgc3VibGluZWFnZSBncm91cApQSEUuZ2VvLnN1YmxpbmVhZ2UgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KHBoZV9jZW50cmUsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5yZWdpb249c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhUUEEucGluZWNvbmUuc3VibGluZWFnZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnJlZ2lvbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQpgYGAKXApNYWtlIHNvbWUgcGxvdHMKYGBge3IsIGZpZy53aWR0aD0xMCwgZmlnLmhlaWdodD04fQojIE1ha2UgaGJhciBwbG90IG9mIHNhbXBsZSBjb3VudHMgYnkgcmVnaW9uCnAucmVnaW9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLmNvdW50LCBhZXMoY291bnQucGVyLnJlZ2lvbixwaGVfY2VudHJlLCBmaWxsPXBoZV9jZW50cmUpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249InN0YWNrIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLHZhbHVlcz1QSEUucmVnaW9uLmNvbHMuYnJldyRyZWdpb24uY29sLCBicmVha3M9UEhFLnJlZ2lvbi5jb2xzLmJyZXckVUtIU0EucmVnaW9uKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5jb3VudCwgYWVzKChjb3VudC5wZXIucmVnaW9uKzEyKSwgcGhlX2NlbnRyZSxsYWJlbD1jb3VudC5wZXIucmVnaW9uKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IlNhbXBsZSBDb3VudCIpICsKICAjY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDEzMCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMjYwKSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTIpKSAKI3AucmVnaW9uLmhiYXJwbG90CgojIG1ha2UgdGVtcG9yYWwgYnViYmxlcGxvdCBvZiBjb3VudHMgYnkgcmVnaW9uCnAucmVnaW9uLnllYXIuYnViYmxlcGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5jb3VudC55ZWFycywgYWVzKGFzLm51bWVyaWMoeWVhciksIHBoZV9jZW50cmUsIGNvbG91cj1waGVfY2VudHJlKSkgKwogIGdlb21fcG9pbnQoYWxwaGE9MC42NSwgYWVzKHNpemU9Y291bnQucGVyLnJlZ2lvbi55ZWFyKSkgKyAKICBnZW9tX2xpbmUoYWxwaGE9MC4yNSkgKwogIGd1aWRlcyhjb2xvdXI9J25vbmUnKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNyxicmVha3M9YygxLDUsMTAsMjUsNTApKSArIAogIGd1aWRlcyhzaXplPWd1aWRlX2xlZ2VuZChucm93PTIsIGRpcmVjdGlvbiA9ICdob3Jpem9udGFsJywgYnlyb3c9VCkpICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iVUtIU0FcblJlZ2lvbiIsdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IlNhbXBsZSBZZWFyIiwgc2l6ZT0iQ291bnQiKSAKI3AucmVnaW9uLnllYXIuYnViYmxlcGxvdAoKIyBPciBhIGJhcnBsb3Qgb2YgbGluZWFnZSBieSB5ZWFyICYgUEhFIHJlZ2lvbj8KcC5yZWdpb24ueWVhci5idWJibGVwbG90LmJhcnBsb3QuZmFjZXQubGluZWFnZSA8LSBQSEUuZ2VvLmNvdW50LnllYXJzLmxpbmVhZ2UgJT4lIHRpZHlyOjpwaXZvdF9sb25nZXIoYyhTUzE0LCBOaWNob2xzKSwgbmFtZXNfdG89IlRQQV9MaW5lYWdlIiwgdmFsdWVzX3RvPSJDb3VudCIpICU+JQogIGdncGxvdChhZXMoeWVhciwgQ291bnQsIGZpbGw9VFBBX0xpbmVhZ2UpKSArIAogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5Jywgd2lkdGg9MC42KSArIAogIGZhY2V0X2dyaWQocGhlX2NlbnRyZX4uLCBzY2FsZXM9J2ZyZWUnKSArCiAgZ3VpZGVzKHNpemU9Z3VpZGVfbGVnZW5kKG5yb3c9MikpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJUUEFcbkxpbmVhZ2UiLHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlKSArCiAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvcj0nd2hpdGUnLCBmaWxsPSd3aGl0ZScsbGluZXR5cGU9InNvbGlkIiksIHN0cmlwLnRleHQueSA9IGVsZW1lbnRfdGV4dChjb2xvciA9ICJncmV5MjUiLCBzaXplPTcsIGFuZ2xlPTApKSAKI3AucmVnaW9uLnllYXIuYnViYmxlcGxvdC5iYXJwbG90LmZhY2V0LmxpbmVhZ2UKCiMgTWFrZSBwcm9wb3J0aW9uYWwgaGJhciBwbG90IG9mIEhJViBzdGF0dXMKcC5yZWdpb24uaGl2LmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLkhJVi5jb3VudHMsIGFlcyhjb3VudC5wZXIucmVnaW9uLmhpdixwaGVfY2VudHJlLGZpbGw9aGl2cG9zKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkhJViArdmUiLHZhbHVlcz1QSEUuaGl2LmNvbHMkaGl2LmNvbHMsIGJyZWFrcz1QSEUuaGl2LmNvbHMkaGl2cG9zKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJISVYgK3ZlIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5ISVYuY291bnRzLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgcGhlX2NlbnRyZSxsYWJlbD1jb3VudC5wZXIucmVnaW9uLmhpdiksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKSArCiAgTlVMTAojcC5yZWdpb24uaGl2LmhiYXJwbG90CgpwLnJlZ2lvbi5vcmllbnRhdGlvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyhjb3VudC5wZXIucmVnaW9uLm9yaWVudGF0aW9uLHBoZV9jZW50cmUsZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iT3JpZW50YXRpb24iLHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9Ik9yaWVudGF0aW9uIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTEpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLmdlby5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCBwaGVfY2VudHJlLGxhYmVsPWNvdW50LnBlci5yZWdpb24ub3JpZW50YXRpb24pLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AucmVnaW9uLm9yaWVudGF0aW9uLmhiYXJwbG90CgpwLnJlZ2lvbi51a2Jvcm4uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uVUtib3JuLCBhZXMoQ291bnQscGhlX2NlbnRyZSxmaWxsPXVrYm9ybikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJVSyBCb3JuIix2YWx1ZXM9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybi5jb2xzLCBicmVha3M9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybikgKwogIGxhYnMoeT0iVUtIU0EgUmVnaW9uIiwgeD0iVUsgQm9ybiIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobnJvdz0zKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5nZW8uVUtib3JuLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgcGhlX2NlbnRyZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5yZWdpb24udWtib3JuLmhiYXJwbG90CgpwLnJlZ2lvbi5Mb25kb24uaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uTG9uZG9uLCBhZXMoQ291bnQscGhlX2NlbnRyZSxmaWxsPWxvbmRvbikpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJMb25kb24iLHZhbHVlcz1QSEUubG9uZG9uLmNvbHMkbG9uZG9uLmNvbHMsIGJyZWFrcz1QSEUubG9uZG9uLmNvbHMkbG9uZG9uKSArCiAgbGFicyh5PSJVS0hTQSBSZWdpb24iLCB4PSJMb25kb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9MykpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuZ2VvLkxvbmRvbiwgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3AucmVnaW9uLkxvbmRvbi5oYmFycGxvdAoKcC5yZWdpb24uQWdlLmhiYXJwbG90IDwtIGdncGxvdChQSEUuZ2VvLkFnZSwgYWVzKENvdW50LHBoZV9jZW50cmUsZmlsbD1hZ2VfZ3JvdXApKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iQWdlXG5Hcm91cCIsdmFsdWVzPVBIRS5BZ2UuY29scyRhZ2VfZ3JvdXAuY29scywgYnJlYWtzPVBIRS5BZ2UuY29scyRhZ2VfZ3JvdXApICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IkFnZSBHcm91cCIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0xKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5nZW8uQWdlLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgcGhlX2NlbnRyZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5yZWdpb24uQWdlLmhiYXJwbG90CmBgYApcCkNvbWJpbmVkIHBsb3QKYGBge3IsIGZpZy53aWR0aD0xMiwgZmlnLmhlaWdodD0xMH0KUEhFLnJlZ2lvbi5jb21iaXBsb3QuMSA8LSBwbG90X2dyaWQocC5yZWdpb24ueWVhci5idWJibGVwbG90LCBwLnJlZ2lvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24uaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24uQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg0LDIsMiwyLDIpLCBzY2FsZT0wLjkpCgpQSEUucmVnaW9uLmNvbWJpcGxvdC4xCmBgYAoKClwKUmVnaW9ucyBhcyBhIGNvbXBsZXggbXVsdGlwYW5lbCBwbG90CmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9NC41fQoKCiMgbGVnZW5kcwpQSEUucmVnaW9uLmNvbWJpcGxvdC4xLmxlZ2VuZHMgPC0gcGxvdF9ncmlkKGdldF9sZWdlbmQocC5yZWdpb24ueWVhci5idWJibGVwbG90KSwgZ2V0X2xlZ2VuZChwLnJlZ2lvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAucmVnaW9uLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIGdldF9sZWdlbmQocC5yZWdpb24uaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIGdldF9sZWdlbmQocC5yZWdpb24uQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNiw0LDQsNCw0KSwgc2NhbGU9MC45NSkKCgojIEFycmFuZ2UgcGxvdHMgdmVydGljYWxseQpwLnllYXIuYnViYmxlcGxvdC5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwueWVhci5idWJibGVwbG90ICsgeC50aGVtZS5zdHJpcCwgcC5yZWdpb24ueWVhci5idWJibGVwbG90ICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCnAucmVnaW9uLmhiYXIuY291bnRzLmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5oYmFycGxvdCArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwLCBwLnJlZ2lvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5yZWdpb24uaGJhci5vcmllbnRhdGlvbi5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwub3JpZW50YXRpb24uaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24ub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBuY29sPTEsIGFsaWduPSJ2IixheGlzPSJsciIsIHJlbF9oZWlnaHRzPWMoMSw3KSkKCnAucmVnaW9uLmhiYXIuaGl2LmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5oaXYuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5yZWdpb24uaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnJlZ2lvbi5oYmFyLkFnZS5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwuQWdlLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAucmVnaW9uLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKIyBDb21iaW5lIHRoZSBwbG90cwpwLnJlZ2lvbi5oYmFyLmNvbWJpLnBsdXMuYWxsIDwtIHBsb3RfZ3JpZChwLnllYXIuYnViYmxlcGxvdC5jb21iaSwgcC5yZWdpb24uaGJhci5jb3VudHMuY29tYmksIHAucmVnaW9uLmhiYXIub3JpZW50YXRpb24uY29tYmksIHAucmVnaW9uLmhiYXIuaGl2LmNvbWJpLCBwLnJlZ2lvbi5oYmFyLkFnZS5jb21iaSwgbnJvdz0xLCByZWxfd2lkdGhzPWMoNiw0LDQsNCw0KSwgbGFiZWxzID0gYygiQSIsIkIiLCJDIiwiRCIsIkUiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgdmp1c3Q9MC4yNSkKIyBhbmQgYWRkIHRoZSBsZWdlbmRzIG9uIHRvcApwLnJlZ2lvbi5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcyA8LSBwbG90X2dyaWQocC5yZWdpb24uaGJhci5jb21iaS5wbHVzLmFsbCwgUEhFLnJlZ2lvbi5jb21iaXBsb3QuMS5sZWdlbmRzLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoNiwxKSwgc2NhbGUgPSAwLjk1KQoKCgpwLnJlZ2lvbi5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcwojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwgIlN1cEZpZzJfVFBBLVBIRV9TYW1wbGUtbWV0YWRpc3Ryb3MtYnktcGhlX3JlZ2lvbithbGwtY29tYmkuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MjQwLCBoZWlnaHQ9MTM1LCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQoKYGBgClwKXApOb3cgbGV0cyBsb29rIGF0IHNvbWUgZ2VuZXRpYyBkYXRhClwKIyMjIE1ha2UgTUwgdHJlZSB3aXRoIHN1YmxpbmVhZ2UgdGlwcG9pbnRzCmBgYHtyfQpUUEEuTUx0cmVlLmdndHJlZS50aXBwb2ludCA8LSBUUEEuTUx0cmVlLmdndHJlZSAlPCslIGRhdGEuZnJhbWUoU2FtcGxlX05hbWU9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFN1YmxpbmVhZ2U9VFBBLm1ldGEyLjEkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2Usc3RyaW5nc0FzRmFjdG9ycyA9IEYpICsgCiAgZ2VvbV90aXBwb2ludChhZXMoY29sb3I9U3VibGluZWFnZSksIHNpemU9MC41LCBhbHBoYT0wLjUsIHNob3cubGVnZW5kID0gRkFMU0UpICsgCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IlN1YmxpbmVhZ2UiLHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpCmBgYApcCkFkZCBtZXRhZGF0YQpgYGB7ciwgZmlnLndpZHRoPTEwLCBmaWcuaGVpZ2h0PTEwfQojIENvbnRpbmVudApwLlRQQS5NTHRyZWUuUEhFIDwtIGdoZWF0bWFwKFRQQS5NTHRyZWUuZ2d0cmVlLnRpcHBvaW50LAogICAgICAgICAgICAgICBUUEEucmF3c2VxLmNvbnRpbmVudHMucCwgY29sb3I9TlVMTCx3aWR0aD0wLjA3NSxvZmZzZXQ9MC4wMDAwMDAyNSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iQ29udGluZW50Iix2YWx1ZXM9Y29udGluZW50YWwuY29scy5icmV3MiRjb250aW5lbnQuY29sLCBicmVha3M9Y29udGluZW50YWwuY29scy5icmV3MiRDb250aW5lbnQsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gMSxuY29sPTIpKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBuZXdfc2NhbGVfZmlsbCgpCgojIGlzIFVLCnAuVFBBLk1MdHJlZS5QSEUgPC0gZ2hlYXRtYXAocC5UUEEuTUx0cmVlLlBIRSwKICAgICAgICAgICAgICAgVFBBLnJhd3NlcS5VSy5wLCBjb2xvcj1OVUxMLHdpZHRoPTAuMDc1LG9mZnNldD0wLjAwMDAxMDI1LCBjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCxmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJFbmdsYW5kL090aGVyIiwgdmFsdWVzPWMoImJsYWNrIiwiZ3JleTk1IiksIGJyZWFrcz1jKCJFbmdsYW5kIiwiT3RoZXIiKSwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyLG5jb2w9MikpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIG5ld19zY2FsZV9maWxsKCkKCiMgTGluZWFnZQpwLlRQQS5NTHRyZWUuUEhFIDwtIGdoZWF0bWFwKHAuVFBBLk1MdHJlZS5QSEUsVFBBLnJhd3NlcS5MaW5lYWdlLnAsIGNvbG9yPU5VTEwsd2lkdGg9MC4wNzUsb2Zmc2V0PTAuMDAwMDIwMjUsIGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkxpbmVhZ2UiLHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDMsIG5jb2w9MikpICsgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICAgbmV3X3NjYWxlX2ZpbGwoKSArCiAgTlVMTAoKIyBzdWJsaW5lYWdlCnAuVFBBLk1MdHJlZS5QSEUgPC0gZ2hlYXRtYXAocC5UUEEuTUx0cmVlLlBIRSwgZGF0YS5mcmFtZShyb3cubmFtZXM9VFBBLm1ldGEyLjEkU2FtcGxlX05hbWUsIFN1YmxpbmVhZ2U9VFBBLm1ldGEyLjEkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2Usc3RyaW5nc0FzRmFjdG9ycyA9IEYpLCBjb2xvcj1OVUxMLHdpZHRoPTAuMDc1LG9mZnNldD0wLjAwMDAzMDI1LCBjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCxmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJTdWJsaW5lYWdlIix2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDQsIG5jb2w9MykpICsgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICAgbmV3X3NjYWxlX2ZpbGwoKSArCiAgTlVMTApgYGAKXApwbG90CmBgYHtyLCBmaWcud2lkdGg9MTAsIGZpZy5oZWlnaHQ9MTB9CnAuVFBBLk1MdHJlZS5QSEUKCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCAiU3VwRmlnM19UUEEtUEhFX0dsb2JhbF9QaHlsbytVSy1oaWdobGlnaHRzLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE4NSwgaGVpZ2h0PTE2MCwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKYGBgClwKXAojIyMgR2VvZ3JhcGhpYyBkaXN0cmlidXRpb25zIG9mIExpbmVhZ2VzIGFuZCBTdWJsaW5lYWdlcwpXaGF0IGFib3V0IHN1YmxpbmVhZ2VzPwpgYGB7cn0KcC5yZWdpb24uTGluZWFnZS5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLmdlby5MaW5lYWdlLCBhZXMoQ291bnQscGhlX2NlbnRyZSxmaWxsPVRQQV9MaW5lYWdlKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRQQVxuTGluZWFnZSIsdmFsdWVzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZS5jb2wsIGJyZWFrcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UpICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IlRQQSBMaW5lYWdlIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgI2dlb21fdGV4dChkYXRhPVBIRS5nZW8uTGluZWFnZSwgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIE5VTEwKCnAucmVnaW9uLnN1YmxpbmVhZ2UuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uc3VibGluZWFnZSwgYWVzKENvdW50LHBoZV9jZW50cmUsZmlsbD1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsKICBnZW9tX2Jhcmgoc3RhdD0iaWRlbnRpdHkiLCBwb3NpdGlvbj0iZmlsbCIsIHdpZHRoPTAuNjUpICsKICB0aGVtZV9saWdodCgpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J2JvdHRvbScpICsKICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJUUEFcblN1YmxpbmVhZ2UiLHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpICsKICBsYWJzKHk9IlVLSFNBIFJlZ2lvbiIsIHg9IlRQQSBTdWJsaW5lYWdlIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTQpKSArCiAgI2dlb21fdGV4dChkYXRhPVBIRS5nZW8uc3VibGluZWFnZSwgYWVzKGN1bV9mcmFjdC5taWQsIHBoZV9jZW50cmUsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikgKwogIE5VTEwKCmBgYApcCkNvbWJpIHBsb3QgKGdlb2dyYXBoeSBsaW5lYWdlcykKYGBge3J9ClBIRS5yZWdpb24uY29tYmlwbG90LjIubGluZWFnZXMgPC0gcGxvdF9ncmlkKHAucmVnaW9uLnllYXIuYnViYmxlcGxvdCArbGVnZW5kLnN0cmlwLCBwLnJlZ2lvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAgKyBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMTUwKSksIHAucmVnaW9uLkxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICtsZWdlbmQuc3RyaXAsIHAucmVnaW9uLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICtsZWdlbmQuc3RyaXAsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNiwzLDQsNCksIHNjYWxlPTAuOTksIGxhYmVscz1jKCJDIiwiRCIsIkUiLCJGIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCgojIHNlcGFyYXRlIG91dCB0aGUgcGxvdCBmb3IgdGhlIGxlZ2VuZHMKcC5yZWdpb24ueWVhci5idWJibGVwbG90LmxlZ2VuZCA8LSBnZXRfbGVnZW5kKHAucmVnaW9uLnllYXIuYnViYmxlcGxvdCkKcC5yZWdpb24uaGJhcnBsb3QubGVnZW5kIDwtIGdldF9sZWdlbmQocC5yZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKQpwLnJlZ2lvbi5MaW5lYWdlLmhiYXJwbG90LmxlZ2VuZCA8LSBnZXRfbGVnZW5kKHAucmVnaW9uLkxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKQpwLnJlZ2lvbi5zdWJsaW5lYWdlLmhiYXJwbG90LmxlZ2VuZCA8LSBnZXRfbGVnZW5kKHAucmVnaW9uLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKQoKUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcy5sZWdlbmQgPC0gcGxvdF9ncmlkKHAucmVnaW9uLnllYXIuYnViYmxlcGxvdC5sZWdlbmQsIHAucmVnaW9uLmhiYXJwbG90LmxlZ2VuZCwgcC5yZWdpb24uTGluZWFnZS5oYmFycGxvdC5sZWdlbmQsIHAucmVnaW9uLnN1YmxpbmVhZ2UuaGJhcnBsb3QubGVnZW5kLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDYsMyw0LDQpKQoKUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcyA8LSBwbG90X2dyaWQoUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcywgUEhFLnJlZ2lvbi5jb21iaXBsb3QuMi5saW5lYWdlcy5sZWdlbmQsIHJlbF9oZWlnaHRzID0gYyg0LDEpLCBuY29sPTEpCgpQSEUucmVnaW9uLmNvbWJpcGxvdC4yLmxpbmVhZ2VzCmBgYApcCk9LLCBsZXQncyBub3cgYWRkIGEgbWFwIG9mIHRoZXNlIGdlb2dyYXBoaWNhbCBkaXN0cmlidXRpb25zCgpcCkxldCdzIHVzZWQgT05TIHB1Ymxpc2hlZCBzaGFwZSBmaWxlcyAtIHRoZXJlIGlzIG9uZSBhdmFpbGFibGUgdGhhdCBzaG93cyBQdWJsaWMgSGVhbHRoIEVuZ2xhbmQgcmVnaW9uIGJvdW5kYXJpZXMuIApgYGB7cn0KCiMgR2VuZXJhdGUgYXBwcm94aW1hdGUgcmVnaW9uYWwgR1BTIGNvb3JkcwpQSEUucmVnaW9uLkdQUyA8LSBkYXRhLmZyYW1lKAogIHN0cmluZ3NBc0ZhY3RvcnMgPSBGQUxTRSwKICAgICAgICAgIHBoZV9jZW50cmUgPSBjKCJFYXN0IE1pZGxhbmRzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJFYXN0IG9mIEVuZ2xhbmQiLCJMb25kb24iLCJOb3J0aCBFYXN0IiwiTm9ydGggV2VzdCIsCiAgICAgICAgICAgICAgICAgICAgICAgICAiU291dGggRWFzdCIsIlNvdXRoIFdlc3QiLCJXZXN0IE1pZGxhbmRzIiwKICAgICAgICAgICAgICAgICAgICAgICAgICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIsIlVLIChub3QgRW5nbGFuZCkiLCJOb3QgS25vd24iKSwKICAgICAgICAgICAgTG9uZ2l0dWRlID0gYygtMC43LDAuNSwtMC4yLC0xLjksLTIuNCwKICAgICAgICAgICAgICAgICAgICAgICAgIDAuMDUsLTIuOSwtMiwtMC44LDAuMSwwLjYzKSwKICAgICAgICAgICBMYXRpdHVkZSA9IGMoNTIuOSw1Mi40LDUxLjUsNTUsNTMuNywKICAgICAgICAgICAgICAgICAgICAgICAgIDUxLjEsNTEsNTIuNiw1My44LDU0LjcsNTQuMSkKICApICAKUEhFLnJlZ2lvbi5HUFMgPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLCBQSEUuZ2VvLkxpbmVhZ2VbUEhFLmdlby5MaW5lYWdlJFRQQV9MaW5lYWdlPT0iU1MxNCIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTKVs0XSA8LSAiU1MxNCIKUEhFLnJlZ2lvbi5HUFMgPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLCBQSEUuZ2VvLkxpbmVhZ2VbUEhFLmdlby5MaW5lYWdlJFRQQV9MaW5lYWdlPT0iTmljaG9scyIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTKVs1XSA8LSAiTmljaG9scyIKUEhFLnJlZ2lvbi5HUFNbaXMubmEoUEhFLnJlZ2lvbi5HUFMpXSA8LSAwCgpQSEUucmVnaW9uLkdQUyA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMsIFBIRS5nZW8uTGluZWFnZVtQSEUuZ2VvLkxpbmVhZ2UkVFBBX0xpbmVhZ2U9PSJTUzE0IixjKCJwaGVfY2VudHJlIiwidG90YWwucmVnaW9uIildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTKVs2XSA8LSAiUmVnaW9uX0NvdW50IgoKUEhFLnJlZ2lvbi5HUFMkcmFkaXVzIDwtIDAuNSooMS0xL3NxcnQoUEhFLnJlZ2lvbi5HUFMkUmVnaW9uX0NvdW50KSkKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgSW1wb3J0IGRhdGFmaWxlIGZyb20gaHR0cHM6Ly9nZW9wb3J0YWwuc3RhdGlzdGljcy5nb3YudWsvZGF0YXNldHMvcHVibGljLWhlYWx0aC1lbmdsYW5kLWNlbnRyZXMtZGVjZW1iZXItMjAxNi1mdWxsLWNsaXBwZWQtYm91bmRhcmllcy1pbi1lbmdsYW5kL2V4cGxvcmU/bG9jYXRpb249NTIuOTUwMDAwJTJDLTIuMDAwMDAwJTJDNi44OAoKVUsuc2hhcGVmaWxlIDwtIHJlYWRPR1IoZHNuPVVLLnB1YmxpY2hlYWx0aC5zaGFwZWZpbGUuZGF0YSkKCiNSZXNoYXBlIGZvciBnZ3Bsb3QyIHVzaW5nIHRoZSBCcm9vbSBwYWNrYWdlClVLLm1hcGRhdGEgPC0gdGlkeShVSy5zaGFwZWZpbGUsIHJlZ2lvbj0icGhlYzE2bm0iKQoKI1VLLmdnIDwtIGdncGxvdCgpICsgZ2VvbV9wb2x5Z29uKGRhdGEgPSBVSy5tYXBkYXRhLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBjb2xvciA9ICIjRkZGRkZGIiwgc2l6ZSA9IDAuMjUpClVLLmdnIDwtIGdncGxvdCgpICsgZ2VvbV9wb2x5Z29uKGRhdGEgPSBVSy5tYXBkYXRhLCBhZXMoeCA9IGxvbmcsIHkgPSBsYXQsIGdyb3VwID0gZ3JvdXApLCBjb2xvcj0iZ3JleTI1IiwgZmlsbD0iZ3JleTkwIiwgc2l6ZSA9IDAuMDc1KQoKVUsuZ2cgPC0gVUsuZ2cgKyBjb29yZF9maXhlZCgxKSArIHRoZW1lX25vdGhpbmcoKQojVUsuZ2cKCiMgQ29udmVydCBVSyByZWdpb25zIHRvIGJlIGNvbXBhdGlibGUgd2l0aCBtYXAKIyBGaXJzdCBmaW5kIGNlbnRyZSBwb2ludCBmb3IgZWFjaCByZWdpb24KVUsubWFwZGF0YS5yZWdpb25zLm1lYW5jb29yZHMgPC0gVUsubWFwZGF0YSAlPiUgZHBseXI6Omdyb3VwX2J5KGlkKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKG1lYW4ubGF0PW1lYW4obGF0KSwgbWVhbi5sb25nPW1lZGlhbihsb25nKSkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKQpjb2xuYW1lcyhVSy5tYXBkYXRhLnJlZ2lvbnMubWVhbmNvb3JkcylbMV0gPC0gInBoZV9jZW50cmUiCgpQSEUucmVnaW9uLkdQUy51a21hcCA8LSBkcGx5cjo6bGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLCBVSy5tYXBkYXRhLnJlZ2lvbnMubWVhbmNvb3JkcywgYnk9InBoZV9jZW50cmUiKQoKIyBBZGQgYXJ0aWZpY2lhbCBsb2NhdGlvbiBmb3IgJ25vdCBrbm93bicKUEhFLnJlZ2lvbi5HUFMudWttYXBbUEhFLnJlZ2lvbi5HUFMudWttYXAkcGhlX2NlbnRyZT09Ik5vdCBLbm93biIsIm1lYW4ubGF0Il0gPC0gNjAwMDAwClBIRS5yZWdpb24uR1BTLnVrbWFwW1BIRS5yZWdpb24uR1BTLnVrbWFwJHBoZV9jZW50cmU9PSJOb3QgS25vd24iLCJtZWFuLmxvbmciXSA8LSA1NTAwMDAKCiMgU2hpZnQgIlNvdXRoIEVhc3QiIHNsaWdodGx5IHRvIHJlZHVjZSB0aGUgb3ZlcmxhcCB3aXRoIExvbmRvbgpQSEUucmVnaW9uLkdQUy51a21hcFtQSEUucmVnaW9uLkdQUy51a21hcCRwaGVfY2VudHJlPT0iU291dGggRWFzdCIsIm1lYW4ubG9uZyJdIDwtIDQ3NTAwMAojIFNoaWZ0ICJFYXN0IG9mIEVuZ2xhbmQgRWFzdCIgc2xpZ2h0bHkgdG8gcmVkdWNlIHRoZSBvdmVybGFwIHdpdGggTG9uZG9uIApQSEUucmVnaW9uLkdQUy51a21hcFtQSEUucmVnaW9uLkdQUy51a21hcCRwaGVfY2VudHJlPT0iRWFzdCBvZiBFbmdsYW5kIiwibWVhbi5sYXQiXSA8LSAyNzUwMDAKCiMgTm90IGdvaW5nIHRvIHRyeSBwbG90dGluZyB0aGUgMiBzYW1wbGVzIGZyb20gZWxzZXdoZXJlIGluIHRoZSBVSywgc28gcmVtb3ZlIHRoYXQgcm93ClBIRS5yZWdpb24uR1BTLnVrbWFwIDwtIFBIRS5yZWdpb24uR1BTLnVrbWFwW1BIRS5yZWdpb24uR1BTLnVrbWFwJHBoZV9jZW50cmUgIT0gIlVLIChub3QgRW5nbGFuZCkiLF0KCiMgQ3JlYXRlIHJhZGl1cyB2YXJpYWJsZSBmb3IgcGxvdHRpbmcgcGllIHNpemVzICh1c2UgbG9nMTAobikqMjAsMDAwKQpQSEUucmVnaW9uLkdQUy51a21hcCRyYWRpdXMuVUsgPC0gbG9nMTAoUEhFLnJlZ2lvbi5HUFMudWttYXAkUmVnaW9uX0NvdW50KSoyMDAwMAoKI1BIRS5nZW8uY291bnQueWVhcnMubGluZWFnZQoKVUsuZ2cuc2NhdHRlcnBpZSA8LSBVSy5nZyArIGdlb21fc2NhdHRlcnBpZShkYXRhPVBIRS5yZWdpb24uR1BTLnVrbWFwLCBhZXMobWVhbi5sb25nLCBtZWFuLmxhdCwgZ3JvdXA9cGhlX2NlbnRyZSwgcj1yYWRpdXMuVUspLCBhbHBoYT0wLjg1LCBjb2xvcj1OQSwgY29scz1jKCJOaWNob2xzIiwiU1MxNCIpKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRQQVxuTGluZWFnZSIsdmFsdWVzPVRQQV9MaW5lYWdlLmNvbHMkTGluZWFnZS5jb2wsIGJyZWFrcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UpICsgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSJ0b3AiKQoKVUsuZ2cuc2NhdHRlcnBpZSA8LSBVSy5nZy5zY2F0dGVycGllICsgZ2VvbV9zY2F0dGVycGllX2xlZ2VuZChQSEUucmVnaW9uLkdQUy51a21hcFshaXMubmEoUEhFLnJlZ2lvbi5HUFMudWttYXAkbWVhbi5sYXQpLCJyYWRpdXMuVUsiXSwgbGFiZWxsZXI9ZnVuY3Rpb24oeCkgcm91bmQoKDEwXih4LzIwMDAwKSksMCksIG49MywgeD0xNTAwMDAsIHk9NTAwMDAwKQoKVUsuZ2cuc2NhdHRlcnBpZSA8LSBVSy5nZy5zY2F0dGVycGllICsgdGhlbWVfbm90aGluZygpCgojPyBBZGQgbGFiZWxzClVLLmdnLnNjYXR0ZXJwaWUubGFicyA8LSBVSy5nZy5zY2F0dGVycGllICsgZ2VvbV9sYWJlbF9yZXBlbChkYXRhPVBIRS5yZWdpb24uR1BTLnVrbWFwWyFpcy5uYShQSEUucmVnaW9uLkdQUy51a21hcCRtZWFuLmxhdCksXSwgYWVzKG1lYW4ubG9uZywgbWVhbi5sYXQsIGxhYmVsPXBoZV9jZW50cmUpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIG51ZGdlX3ggPSA1MDAwMCwgbnVkZ2VfeSA9IC0yNTAwMCwgc2VnbWVudC5zaXplICA9IDAuMSkgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArIAogIHRoZW1lLnRleHQuc2l6ZSArCiAgdGhlbWVfbm90aGluZygpCgpVSy5nZy5zY2F0dGVycGllLmxhYnMKYGBgClwKXApOb3cgZG8gYW4gZXF1aXZhbGVudCBwbG90IGZvciBzdWJsaW5lYWdlcwpgYGB7cn0KUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIFBIRS5yZWdpb24uR1BTLnVrbWFwCgoKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSIxIixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxMV0gPC0gIjEiClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iMiIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTJdIDwtICIyIgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjMiLGMoInBoZV9jZW50cmUiLCJDb3VudCIpXSwgYnk9InBoZV9jZW50cmUiKQpjb2xuYW1lcyhQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pWzEzXSA8LSAiMyIKUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluIDwtIGxlZnRfam9pbihQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4sIFBIRS5nZW8uc3VibGluZWFnZVtQSEUuZ2VvLnN1YmxpbmVhZ2UkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2U9PSI2IixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxNF0gPC0gIjYiClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiA8LSBsZWZ0X2pvaW4oUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluLCBQSEUuZ2VvLnN1YmxpbmVhZ2VbUEhFLmdlby5zdWJsaW5lYWdlJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlPT0iOCIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTVdIDwtICI4IgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjE0IixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxNl0gPC0gIjE0IgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjE1IixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxN10gPC0gIjE1IgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IjE2IixjKCJwaGVfY2VudHJlIiwiQ291bnQiKV0sIGJ5PSJwaGVfY2VudHJlIikKY29sbmFtZXMoUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluKVsxOF0gPC0gIjE2IgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4gPC0gbGVmdF9qb2luKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiwgUEhFLmdlby5zdWJsaW5lYWdlW1BIRS5nZW8uc3VibGluZWFnZSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09IlNpbmdsZXRvbiIsYygicGhlX2NlbnRyZSIsIkNvdW50IildLCBieT0icGhlX2NlbnRyZSIpCmNvbG5hbWVzKFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbilbMTldIDwtICJTaW5nbGV0b24iClBIRS5yZWdpb24uR1BTLnVrbWFwLnN1Ymxpbltpcy5uYShQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4pXSA8LSAwCgojIE1vc3Qgc2FtcGxlcyBhcmUgZWl0aGVyIHN1YmxpbmVhZ2UgMSBvciAxNC4gTGV0J3MgY3JlYXRlIGEgY291bnQgb2Ygc2FtcGxlcyB0aGF0IGFyZSBuZWl0aGVyLgpQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4kYE90aGVyIFN1YmxpbmVhZ2VzYCA8LSBzYXBwbHkoMTpucm93KFBIRS5yZWdpb24uR1BTLnVrbWFwLnN1YmxpbiksIGZ1bmN0aW9uICh4KSBQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4kUmVnaW9uX0NvdW50W3hdLXN1bShQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4kYDFgW3hdLCBQSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW4kYDE0YFt4XSkpIAoKCgpVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgPC0gVUsuZ2cgKyBnZW9tX3NjYXR0ZXJwaWUoZGF0YT1QSEUucmVnaW9uLkdQUy51a21hcC5zdWJsaW5bUEhFLnJlZ2lvbi5HUFMudWttYXAuc3VibGluJG1lYW4ubG9uZyE9MCxdLCBhZXMobWVhbi5sb25nLCBtZWFuLmxhdCwgZ3JvdXA9cGhlX2NlbnRyZSwgcj1yYWRpdXMuVUspLCBhbHBoYT0wLjg1LCBjb2xvcj1OQSwgY29scz1jKCIxIiwiMTQiLCJPdGhlciBTdWJsaW5lYWdlcyIpKSArIAogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRQQVxuU3VibGluZWFnZSIsdmFsdWVzPWMoIiNGQzkyNzIiLCIjQkNCRERDIiwgImdyZXk1MCIpLCBicmVha3M9YygiMSIsIjE0IiwiT3RoZXIgU3VibGluZWFnZXMiKSkKCiMgYWRkIGxlZ2VuZApVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgPC0gVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlICsgZ2VvbV9zY2F0dGVycGllX2xlZ2VuZChQSEUucmVnaW9uLkdQUy51a21hcFshaXMubmEoUEhFLnJlZ2lvbi5HUFMudWttYXAkbWVhbi5sYXQpLCJyYWRpdXMuVUsiXSwgbGFiZWxsZXI9ZnVuY3Rpb24oeCkgcm91bmQoKDEwXih4LzIwMDAwKSksMCksIG49MywgeD0xNTAwMDAsIHk9NTAwMDAwKQoKI1VLLmdnLnNjYXR0ZXJwaWUgPC0gVUsuZ2cuc2NhdHRlcnBpZSArIHgudGhlbWUuc3RyaXAgKyB5LnRoZW1lLnN0cmlwClVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSA8LSBVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UgKyB0aGVtZV9ub3RoaW5nKCkKCiM/IEFkZCBsYWJlbHMKVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlIDwtIFVLLmdnLnNjYXR0ZXJwaWUuc3VibGluZWFnZSArIGdlb21fbGFiZWxfcmVwZWwoZGF0YT1QSEUucmVnaW9uLkdQUy51a21hcFshaXMubmEoUEhFLnJlZ2lvbi5HUFMudWttYXAkbWVhbi5sYXQpLF0sIGFlcyhtZWFuLmxvbmcsIG1lYW4ubGF0LCBsYWJlbD1waGVfY2VudHJlKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBudWRnZV94ID0gNTAwMDAsIG51ZGdlX3kgPSAtMjUwMDAsIHNlZ21lbnQuc2l6ZSAgPSAwLjEpICsKICB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSwgbGVnZW5kLnBvc2l0aW9uPSJib3R0b20iKSArIAogIHRoZW1lLnRleHQuc2l6ZSArCiAgdGhlbWVfbm90aGluZygpCgoKVUsuZ2cuc2NhdHRlcnBpZS5zdWJsaW5lYWdlCmBgYAoKXApDb21iaW5lZCBtYXAgcGxvdApgYGB7cn0KVUsuZ2cuc2NhdHRlcnBpZS5jb21iaSA8LSBwbG90X2dyaWQoVUsuZ2cuc2NhdHRlcnBpZS5sYWJzLCBVSy5nZy5zY2F0dGVycGllLnN1YmxpbmVhZ2UsIG5jb2w9MiwgbGFiZWxzID0gYygiQSIsIkIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSkKClVLLmdnLnNjYXR0ZXJwaWUuY29tYmkKYGBgClwKXApQbG90IGluIGNvbWJpbmF0aW9uIHdpdGggYmFycGxvdHMKYGBge3IsIGZpZy5oZWlnaHQ9OCwgZmlnLndpZHRoPTh9CnBsb3RfZ3JpZChVSy5nZy5zY2F0dGVycGllLmNvbWJpLCBQSEUucmVnaW9uLmNvbWJpcGxvdC4yLmxpbmVhZ2VzLCBucm93PTIsIHJlbF9oZWlnaHRzPWMoNCw1KSkKCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCJGaWcyX1RQQS1QSEVfTWFwLUxpbmVhZ2UrQmFycGxvdHMuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MTkwLCBoZWlnaHQ9MTg1LCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQpgYGAKXApcCiMjIyBBbmFseXNpcyBieSBzdWJsaW5lYWdlClwKTm93IGxldHMgc3RhcnQgZXhwbG9yaW5nIGhvdyBzYW1wbGVzIGFyZSBkaXN0cmlidXRlZCBieSBzdWJsaW5lYWdlCgpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZCA8LSBQSEUubWV0YWRhdGEubGlua2VkClBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UgPC0gZmFjdG9yKFBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGxldmVscz1yZXYoYXMuY2hhcmFjdGVyKHNvcnQodW5pcXVlKFBIRS5tZXRhZGF0YS5saW5rZWQkVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSkpKQoKUEhFLkxpbmVhZ2UuY291bnQgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQV9MaW5lYWdlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbD1zdW0oQ291bnQpLCBwZXJjPShDb3VudC90b3RhbCkqMTAwKQoKUEhFLnN1Ymxpbi5jb3VudCA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsPXN1bShDb3VudCksIHBlcmM9KENvdW50L3RvdGFsKSoxMDApCgpQSEUuZ2VvLnN1Ymxpbi55ZWFycyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UseWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpCgoKIyMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBzdWJsaW5lYWdlIGdyb3VwcwoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IGdlbmRlciBvcmllbnRhdGlvbgpQSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGdlbmRlcl9vcmllbnRhdGlvbikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MoZ2VuZGVyX29yaWVudGF0aW9uKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgoKIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGFib3V0IFVLIGJvcm4KUEhFLnN1YmxpbmVhZ2UuVUtib3JuIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgdWtib3JuKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgI2RwbHlyOjphcnJhbmdlKGRlc2ModWtib3JuKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2ModWtib3JuKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCiAgCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBMb25kb24gYmFzZWQKUEhFLnN1YmxpbmVhZ2UuTG9uZG9uIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgbG9uZG9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhsb25kb24pLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBBZ2UgZ3JvdXAKUEhFLnN1YmxpbmVhZ2UuQWdlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgYWdlX2dyb3VwKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhhZ2VfZ3JvdXApLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBhYm91dCBISVYgZ3JvdXAKUEhFLnN1YmxpbmVhZ2UuSElWIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgaGl2cG9zKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZSh0b3RhbC5zdWJsaW49c3VtKENvdW50KSkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhoaXZwb3MpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCiMgR2VuZXJhdGUgc29tZSBzdGF0cyBieSBQSEUgUmVnaW9uClBIRS5zdWJsaW5lYWdlLlBIRWNlbnRyZSA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHBoZV9jZW50cmUpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZShkZXNjKHBoZV9jZW50cmUpLCAuYnlfZ3JvdXA9VCkgJT4lCiAgZHBseXI6Om11dGF0ZShmcmFjdGlvbj1Db3VudC90b3RhbC5zdWJsaW4sIGN1bV9mcmFjdD1jdW1zdW0oZnJhY3Rpb24pLCBjdW1fZnJhY3QubWlkID0gY3VtX2ZyYWN0LShmcmFjdGlvbi8yKSkKCmBgYAoKXApQbG90IGJ5IHN1YmxpbmVhZ2UKYGBge3J9CnAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QgPC0gZ2dwbG90KFBIRS5nZW8uc3VibGluLnllYXJzLCBhZXMoYXMubnVtZXJpYyh5ZWFyKSwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGNvbG91cj1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNjUsIGFlcyhzaXplPUNvdW50KSkgKyAKICBnZW9tX2xpbmUoYWxwaGE9MC4yNSkgKwogIGd1aWRlcyhjb2xvdXI9J25vbmUnKSArCiAgc2NhbGVfc2l6ZV9hcmVhKG1heF9zaXplID0gNyxicmVha3M9YygxLDUsMTAsMjUsNTApKSArIAogIGd1aWRlcyhzaXplPWd1aWRlX2xlZ2VuZChucm93PTIsIGRpcmVjdGlvbiA9ICdob3Jpem9udGFsJywgYnlyb3c9VCkpICsKICB0aGVtZV9saWdodCgpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iVFBBXG5TdWJsaW5lYWdlIix2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9IlNhbXBsZSBZZWFyIiwgc2l6ZT0iQ291bnQiKSAKI3Auc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QKCnAuc3VibGluZWFnZS5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1Ymxpbi5jb3VudCwgYWVzKENvdW50LFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGZpbGw9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249InN0YWNrIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlRQQVxuU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJTYW1wbGUgQ291bnQiKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLnN1Ymxpbi5jb3VudCwgYWVzKChDb3VudCsxMiksIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICAjY29vcmRfY2FydGVzaWFuKHhsaW09YygwLDIwMCkpICsKICBjb29yZF9jYXJ0ZXNpYW4oeGxpbT1jKDAsMjYwKSkgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTIpKQojcC5zdWJsaW5lYWdlLmhiYXJwbG90IAoKcC5zdWJsaW5lYWdlLm9yaWVudGF0aW9uLmhiYXJwbG90IDwtIGdncGxvdChQSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyh5PVRQQS5waW5lY29uZS5zdWJsaW5lYWdlLHg9Q291bnQsZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iT3JpZW50YXRpb24iLHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsKICBsYWJzKHk9IlRQQSBTdWJsaW5lYWdlIiwgeD0iT3JpZW50YXRpb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MSkpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMsIGFlcyhjdW1fZnJhY3QubWlkLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5yZWdpb24ub3JpZW50YXRpb24uaGJhcnBsb3QKCnAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5zdWJsaW5lYWdlLkhJViwgYWVzKHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHg9Q291bnQsZmlsbD1oaXZwb3MpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdib3R0b20nKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iSElWICt2ZSIsdmFsdWVzPVBIRS5oaXYuY29scyRoaXYuY29scywgYnJlYWtzPVBIRS5oaXYuY29scyRoaXZwb3MpICsKICBsYWJzKHk9IlRQQSBTdWJsaW5lYWdlIiwgeD0iSElWICt2ZSIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0xKSkgKyAKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluZWFnZS5ISVYsIGFlcyhjdW1fZnJhY3QubWlkLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQojcC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdAoKcC5zdWJsaW5lYWdlLnVrYm9ybi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1YmxpbmVhZ2UuVUtib3JuLCBhZXMoeT1UUEEucGluZWNvbmUuc3VibGluZWFnZSx4PUNvdW50LGZpbGw9dWtib3JuKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLXG5ib3JuIix2YWx1ZXM9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybi5jb2xzLCBicmVha3M9UEhFLnVrYm9ybi5jb2xzJHVrYm9ybikgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJVSyBib3JuIikgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChucm93PTMpKSArCiAgZ2VvbV90ZXh0KGRhdGE9UEhFLnN1YmxpbmVhZ2UuVUtib3JuLCBhZXMoY3VtX2ZyYWN0Lm1pZCwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQpLCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGluaGVyaXQuYWVzID0gRikKI3Auc3VibGluZWFnZS51a2Jvcm4uaGJhcnBsb3QKCnAuc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QgPC0gZ2dwbG90KFBIRS5zdWJsaW5lYWdlLkFnZSwgYWVzKHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIHg9Q291bnQgLGZpbGw9YWdlX2dyb3VwKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkFnZVxuR3JvdXAiLHZhbHVlcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwLmNvbHMsIGJyZWFrcz1QSEUuQWdlLmNvbHMkYWdlX2dyb3VwKSArCiAgbGFicyh5PSJUUEEgU3VibGluZWFnZSIsIHg9IkFnZSBHcm91cCIpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0xKSkgKwogIGdlb21fdGV4dChkYXRhPVBIRS5zdWJsaW5lYWdlLkFnZSwgYWVzKGN1bV9mcmFjdC5taWQsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLGxhYmVsPUNvdW50KSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpCiNwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90CgoKcC5zdWJsaW5lYWdlLlBIRXJlZ2lvbi5oYmFycGxvdCA8LSBnZ3Bsb3QoUEhFLnN1YmxpbmVhZ2UuUEhFY2VudHJlLCBhZXMoeT1UUEEucGluZWNvbmUuc3VibGluZWFnZSwgeD1Db3VudCwgZmlsbD1waGVfY2VudHJlKSkgKwogIGdlb21fYmFyaChzdGF0PSJpZGVudGl0eSIsIHBvc2l0aW9uPSJmaWxsIiwgd2lkdGg9MC42NSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLHZhbHVlcz1QSEUucmVnaW9uLmNvbHMuYnJldyRyZWdpb24uY29sLCBicmVha3M9UEhFLnJlZ2lvbi5jb2xzLmJyZXckUEhFLnJlZ2lvbikgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJVS0hTQSBSZWdpb24iKSArCiAgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5yb3c9NCkpICsKICBnZW9tX3RleHQoZGF0YT1QSEUuc3VibGluZWFnZS5QSEVjZW50cmUsIGFlcyhjdW1fZnJhY3QubWlkLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSxsYWJlbD1Db3VudCksIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbiwgaW5oZXJpdC5hZXMgPSBGKQoKYGBgClwKTG9vayBhdCBob3cgc3VibGluZWFnZXMgYXJlIGRpc3RyaWJ1dGVkIGJ5IHJlZ2lvbiAoc3VibGluZWFnZS1jZW50cmljKQpgYGB7cn0KcC5zdWJsaW5lYWdlLlBIRXJlZ2lvbi5oYmFycGxvdApgYGAKClwKQ29tYmluZSBwYXRpZW50IG1ldGFkYXRhIGludG8gYSBwbG90CmBgYHtyLCBmaWcud2lkdGg9MTIsIGZpZy5oZWlnaHQ9MTB9CiNQSEUuc3VibGluZWFnZXMuY29tYmlwbG90LjEgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QsIHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuUEhFcmVnaW9uLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLnVrYm9ybi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5BZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDMsMiwyLDIsMiwyLDIpLCBzY2FsZT0wLjkpCgojUEhFLnN1YmxpbmVhZ2VzLmNvbWJpcGxvdC4xIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LCBwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5QSEVyZWdpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBucm93PTEsIGFsaWduPSJoIiwgcmVsX3dpZHRocz1jKDMsMiwyLDIsMiw0KSwgc2NhbGU9MC45KQoKUEhFLnN1YmxpbmVhZ2VzLmNvbWJpcGxvdC4xIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LCBwLnN1YmxpbmVhZ2UuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2Uub3JpZW50YXRpb24uaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwLCBwLnN1YmxpbmVhZ2UuaGl2LmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAsIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNCwyLDIsMiwyKSwgc2NhbGU9MC45KQoKUEhFLnN1YmxpbmVhZ2VzLmNvbWJpcGxvdC4xIAoKYGBgCgoKXApMZXRzIGFkZCB0aGUgJ2FsbCcgcm93IGFnYWluIHRvIHRoZSAnYnkgc3VibGluZWFnZScgcGxvdApgYGB7ciwgZmlnLmhlaWdodD01LCBmaWcud2lkdGg9MTJ9CiMgbGVnZW5kcwpQSEUuc3VibGluZWFnZS5jb21iaXBsb3QuMS5sZWdlbmRzIDwtIHBsb3RfZ3JpZChnZXRfbGVnZW5kKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QpLCBnZXRfbGVnZW5kKHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXApLCBnZXRfbGVnZW5kKHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwKSwgZ2V0X2xlZ2VuZChwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCksIG5yb3c9MSwgYWxpZ249ImgiLCByZWxfd2lkdGhzPWMoNiw0LDQsNCw0KSwgc2NhbGU9MC45NSkKCiMgcmVnaW9ucwojUEhFLnN1YmxpbmVhZ2UuY29tYmlwbG90LjEubm9sZWdlbmQgPC0gcGxvdF9ncmlkKHAuc3VibGluZWFnZS55ZWFyLmJ1YmJsZXBsb3QgKyBsZWdlbmQuc3RyaXAsIHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIHAuc3VibGluZWFnZS5oaXYuaGJhcnBsb3QgKyB5LnRoZW1lLnN0cmlwICsgbGVnZW5kLnN0cmlwLCBwLnN1YmxpbmVhZ2UuQWdlLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCArIGxlZ2VuZC5zdHJpcCwgbnJvdz0xLCBhbGlnbj0iaCIsIHJlbF93aWR0aHM9Yyg0LDIsMiwyLDIpLCBzY2FsZT0wLjkpCgojIE9yIGRvIGl0IHZlcnRpY2FsbHkKcC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdC5jb21iaSA8LSBwbG90X2dyaWQocC5hbGwueWVhci5idWJibGVwbG90ICsgeC50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdCArIGxlZ2VuZC5zdHJpcCwgbmNvbD0xLCBhbGlnbj0idiIsYXhpcz0ibHIiLCByZWxfaGVpZ2h0cz1jKDEsNykpCgpwLnN1YmxpbmVhZ2UuaGJhci5jb3VudHMuY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5zdWJsaW5lYWdlLmhiYXIub3JpZW50YXRpb24uY29tYmkgPC0gcGxvdF9ncmlkKHAuYWxsLm9yaWVudGF0aW9uLmhiYXJwbG90ICsgeC50aGVtZS5zdHJpcCArIHkudGhlbWUuc3RyaXAsIHAuc3VibGluZWFnZS5vcmllbnRhdGlvbi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5zdWJsaW5lYWdlLmhiYXIuaGl2LmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5oaXYuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLmhpdi5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKcC5zdWJsaW5lYWdlLmhiYXIuQWdlLmNvbWJpIDwtIHBsb3RfZ3JpZChwLmFsbC5BZ2UuaGJhcnBsb3QgKyB4LnRoZW1lLnN0cmlwICsgeS50aGVtZS5zdHJpcCwgcC5zdWJsaW5lYWdlLkFnZS5oYmFycGxvdCArIHkudGhlbWUuc3RyaXAgKyBsZWdlbmQuc3RyaXAsIG5jb2w9MSwgYWxpZ249InYiLGF4aXM9ImxyIiwgcmVsX2hlaWdodHM9YygxLDcpKQoKIyBDb21iaW5lIHRoZSBwbG90cwpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbCA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLnllYXIuYnViYmxlcGxvdC5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIuY291bnRzLmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5vcmllbnRhdGlvbi5jb21iaSwgcC5zdWJsaW5lYWdlLmhiYXIuaGl2LmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5BZ2UuY29tYmksIG5yb3c9MSwgcmVsX3dpZHRocz1jKDcsMyw0LDQsNCksIGxhYmVscz1jKCJBIiwgIkIiLCAiQyIsICJEIiwgIkUiKSxsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCB2anVzdD0xLCBzY2FsZT0wLjk5KQoKIyBhbmQgYWRkIHRoZSBsZWdlbmRzIG9uIHRvcAojcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzIDwtIHBsb3RfZ3JpZChQSEUuc3VibGluZWFnZS5jb21iaXBsb3QuMS5sZWdlbmRzLCBwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbCwgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDEsOSkpCgojIGxlZ2VuZHMgYmVsb3cKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbCwgUEhFLnN1YmxpbmVhZ2UuY29tYmlwbG90LjEubGVnZW5kcywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDgsMSkpCgoKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzCgpgYGAKClwKXCAKVGhlc2UgcGF0dGVybnMgbG9vayBmYWlybHkgc2ltaWxhciBiZXR3ZWVuIHN1YmxpbmVhZ2VzLCBhbmQgKGFwYXJ0IGZyb20gMSAmIDE0KSB0aGUgZ3JvdXBzIGFyZSB2ZXJ5IHNtYWxsLiBIb3dldmVyLCBzdWJsaW5lYWdlIDE0IGRvZXMgYXBwZWFyIHRvIGhhdmUgYSBoaWdoZXIgcHJvcG9ydGlvbiBvZiBNU00gY29tcGFyZWQgdG8gc3VibGluZWFnZSAxIGFuZCBvdGhlcnMuIExldCdzIHRlc3QgdGhhdCBmb3JtYWxseSB1c2luZyAyeDIgZmlzaGVyJ3MgdGVzdHMKXApgYGB7cn0KUEhFLk1TTS5jb3VudHMuYWxsIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShpcy5NU00sIC5kcm9wPUYpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZSgoaXMuTVNNKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpCgpQSEUuc3VibGluZWFnZS5NU00uY291bnRzIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSxpcy5NU00sIC5kcm9wPUYpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLnN1Ymxpbj1zdW0oQ291bnQpKSAlPiUKICBkcGx5cjo6YXJyYW5nZSgoaXMuTVNNKSwgLmJ5X2dyb3VwPVQpICU+JQogIGRwbHlyOjptdXRhdGUoZnJhY3Rpb249Q291bnQvdG90YWwuc3VibGluLCBjdW1fZnJhY3Q9Y3Vtc3VtKGZyYWN0aW9uKSwgY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICMlPiUKICAjZHBseXI6OmZpbHRlcighaXMubmEoaXMuTVNNKSkKCgpQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyIDwtIFBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMgJT4lIGRwbHlyOjpzZWxlY3QoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGlzLk1TTSwgQ291bnQpICU+JQogIHRpZHlyOjpwaXZvdF93aWRlcihuYW1lc19mcm9tID0gaXMuTVNNLCB2YWx1ZXNfZnJvbT1Db3VudCkgJT4lCiAgZHBseXI6Om11dGF0ZShNU009cmVwbGFjZV9uYShNU00sIDApLCBPdGhlcj1yZXBsYWNlX25hKE90aGVyLCAwKSwgVG90YWw9c3VtKE1TTSxPdGhlcikpICU+JQogICNkcGx5cjo6c2VsZWN0KC1gTkFgKSAlPiUKICBkcGx5cjo6ZmlsdGVyKFRvdGFsIT0wKQogIAoKUEhFLnN1YmxpbmVhZ2UuTVNNLnB2YWwgPC0gZGF0YS5mcmFtZShUUEEucGluZWNvbmUuc3VibGluZWFnZT1QSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBwLmZpc2hlcj1zYXBwbHkoMTpucm93KFBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIpLCBmdW5jdGlvbiAoeCkgZmlzaGVyLnRlc3QobWF0cml4KGFzLm51bWVyaWMoYyhQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyW3gsIk1TTSJdLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIFBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXJbeCwiT3RoZXIiXSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBQSEUuTVNNLmNvdW50cy5hbGxbUEhFLk1TTS5jb3VudHMuYWxsJGlzLk1TTT09Ik1TTSIsIkNvdW50Il0sIFBIRS5NU00uY291bnRzLmFsbFtQSEUuTVNNLmNvdW50cy5hbGwkaXMuTVNNPT0iT3RoZXIiLCJDb3VudCJdKSksbnJvdz0yKSlbWzFdXSksIHN0cmluZ3NBc0ZhY3RvcnM9RikKClBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIgPC0gZHBseXI6OmxlZnRfam9pbihQSEUuc3VibGluZWFnZS5NU00uY291bnRzLndpZGVyLCBQSEUuc3VibGluZWFnZS5NU00ucHZhbCwgYnk9IlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIikKClBIRS5zdWJsaW5lYWdlLk1TTS5jb3VudHMud2lkZXIKYGBgCgoKXApcCiMjIyBWaXN1YWxpc2F0aW9uIG9mIFVLIGdlbm9taWMgcmVsYXRpb25zaGlwcwpcCk9rLCBsZXQncyBtYWtlIGEgdHJlZSBmb3IgZGlzcGxheWluZyB0aGVzZSByZWxhdGlvbnNoaXBzIHVzaW5nIHRoZSBVSyBkYXRhc2V0IG9ubHkKXApGcm9tIHNvbWUgZXhwZXJpbWVudGF0aW9uLCBhICdHcmFwZVRyZWUnIG1pbmltdW0gc3Bhbm5pbmcgbmV0d29yayB3b3JrcyB3ZWxsIGZvciB2aXN1YWxpc2luZyB0aGUgY2xvbmFsaXR5IG9mIHRoZXNlIHBvcHVsYXRpb25zLiBXZSBjYW4gdXNlIGEgU05QLXNjYWxlZCBwaHlsb2dlbnkgYXMgZGlyZWN0IGlucHV0IHRvIEdyYXBlVHJlZSwgYW5kIHRoaXMgd2lsbCBhbGxvdyBicmFuY2hlcyB0byBiZSBzY2FsZWQgYXBwcm9wcmlhdGVseS4gSG93ZXZlciwgYWx0aG91Z2ggYW5ub3RhdGlvbiBpcyBhbGxvd2VkIHdpdGhpbiB0aGUgR3JhcGVUcmVlIHNvZnR3YXJlLCBjb2xvdXJzIG11c3QgYmUgbWFudWFsbHkgZWRpdGVkLiBGaW5hbCBHcmFwZVRyZWUgcGxvdHMgY2FuIHRoZW4gYmUgaW1wb3J0ZWQgYmFjayBpbnRvIFIgZm9yIGNvbWJpbmluZyB3aXRoIG90aGVyIHBsb3RzLiAKXAoKQWx0ZXJuYXRpdmUgdmlzdWFsaXNhdGlvbnMgLSBncmFwZXRyZWU/ClwKVGFrZSB0aGUgNTI2LWdsb2JhbCBwaHlsb2dlbnkgKHNucC1zY2FsZWQgdmVyc2lvbiBmcm9tIHB5amFyKSwgYW5kIHBydW5lIHRvIG9ubHkgaW5jbHVkZSB0aGUgVUsgc3RyYWlucyBmcm9tIHRoaXMgc3R1ZHkgLSB0aGlzIGVuc3VyZXMgdGhlIHRvcG9sb2d5IGlzIGNvbnNpc3RlbnQgYWNjcm9zcyBzdHVkaWVzLiAKYGBge3J9CgpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsgPC0gYXBlOjprZWVwLnRpcChUUEEucHlqYXIudHJlZSwgYXMuY2hhcmFjdGVyKHVubGlzdChQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkR2VvX0NvdW50cnk9PSJVSyIsIlNhbXBsZV9OYW1lIl0pKSkKCmdndHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQudWspCiN3cml0ZS50cmVlKFRQQS5weWphci50cmVlLnN1YnNldC51aywgcGFzdGUwKERhdGFfaW5wdXRfZGlyZWN0b3J5LCJUUEEuVUstb25seS5weWphci4yMDIyLTAyLTAzLnRyZSIpKQoKIyBXcml0ZSBvdXQgYSBtZXRhZGF0YSBzaGVldCBmb3IgdGhlIHJlbGV2YW50IGluZm9ybWF0aW9uClBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwgInllYXIiLCJnZW5kZXJfb3JpZW50YXRpb24iLCJwaGVfY2VudHJlIiwiaGl2cG9zIiwidWtib3JuIiwiVFBBX0xpbmVhZ2UiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIpXQpjb2xuYW1lcyhQSEUubWV0YWRhdGEubGlua2VkLmdyYXBldHJlZSlbMV0gPC0gIklEIgoKI3dyaXRlLnRhYmxlKFBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlLCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LmdyYXBldHJlZS5tZXRhLjIwMjItMDItMDMudHN2IiksIHNlcCA9ICJcdCIsIHF1b3RlPUYsIHJvdy5uYW1lcyA9IEYpCmBgYApcClRyZWUgaW5kZXBlbmRlbnRseSB2aXN1YWxpc2VkIGFuZCBhbm5vdGF0ZWQgdXNpbmcgR3JhcGVUcmVlLgpcCk5vdyBpbXBvcnQgYW5kIGludGVncmF0ZSBHcmFwZVRyZWUgcGxvdCB3aXRoIG1ldGFkYXRhIHBsb3RzLgpgYGB7ciwgZmlnLmhlaWdodD04LCBmaWcud2lkdGg9MTB9CiMgQ29tYmluZSB0aGUgcGxvdHMKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwuQjJGIDwtIHBsb3RfZ3JpZChwLnN1YmxpbmVhZ2UueWVhci5idWJibGVwbG90LmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5jb3VudHMuY29tYmksIHAuc3VibGluZWFnZS5oYmFyLm9yaWVudGF0aW9uLmNvbWJpLCBwLnN1YmxpbmVhZ2UuaGJhci5oaXYuY29tYmksIHAuc3VibGluZWFnZS5oYmFyLkFnZS5jb21iaSwgbnJvdz0xLCByZWxfd2lkdGhzPWMoNyw0LDQsNCw0KSwgbGFiZWxzPWMoIkIiLCAiQyIsICJEIiwgIkUiLCAiRiIpLGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUsIHZqdXN0PTEsIHNjYWxlPTAuOTcpCgojIGxlZ2VuZHMgYmVsb3cKcC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwud2l0aC5sZWdlbmRzLkIyRiA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLmhiYXIuY29tYmkucGx1cy5hbGwuQjJGLCBQSEUuc3VibGluZWFnZS5jb21iaXBsb3QuMS5sZWdlbmRzLCBuY29sPTEsIHJlbF9oZWlnaHRzPWMoNywxKSkKCiNwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGCgoKIyBOb3cgYnJpbmcgaW4gZXh0ZXJuYWxseSBwbG90dGVkIEdyYXBldHJlZQpwLlRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKFRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMuZmlsZSkKcC5UUEEuVUsuR3JhcGV0cmVlLnN1YmxpbmVhZ2VzCgpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGLndpdGguZ3JhcGV0cmVlIDwtIHBsb3RfZ3JpZChwLlRQQS5VSy5HcmFwZXRyZWUuc3VibGluZWFnZXMsIHAuc3VibGluZWFnZS5oYmFyLmNvbWJpLnBsdXMuYWxsLndpdGgubGVnZW5kcy5CMkYsIG5jb2w9MSwgbGFiZWxzPWMoIkEiLCIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgcmVsX2hlaWdodHM9YygzLDUpKSAKCgpwLnN1YmxpbmVhZ2UuaGJhci5jb21iaS5wbHVzLmFsbC53aXRoLmxlZ2VuZHMuQjJGLndpdGguZ3JhcGV0cmVlCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCAiRmlnMV9UUEEtUEhFX1NhbXBsZS1kaXN0cm9zLXN1YmxpbmVhZ2UuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MTkwLCBoZWlnaHQ9MTg1LCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQpgYGAKClwKTWFuYWdlIG90aGVyIEdyYXBlVHJlZSBwbG90cyAoZm9yIGNvbnNpc3RlbmN5KQoKVFBBLVVLLTIwMjItMDItMTYuLU1TVHJlZV8zLXdheS1maWd1cmUuSW5zY2FwZWQtMgpgYGB7cn0KIyBCcmluZyBpbiAzLXdheSBncmFwaGV0cmVlIHBsb3QgKDMgZGlmZmVyZW50IG1ldGFkYXRhIHZhcmlhYmxlcyB1c2luZyB0aGUgc2FtZSBpbnB1dCB0cmVlKQpUUEEuVUsuR3JhcGV0cmVlLjN3YXkgPC0gZ2dkcmF3KCkgKyBkcmF3X2ltYWdlKFRQQS5VSy5HcmFwZXRyZWUuM3dheS5maWxlKQpUUEEuVUsuR3JhcGV0cmVlLjN3YXkKCiNnZ3NhdmUocGFzdGUwKEZpZ3VyZV9vdXRwdXRfZGlyZWN0b3J5LCAiU3VwRmlnNF9UUEEtUEhFX0dyYXBldHJlZS0zd2F5cy4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xNDUsIGhlaWdodD0xODAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKClwKQW5kIGFsc28gZG8gdGhlIEhJViBzdGF0dXMgcGxvdApgYGB7cn0KClRQQS5VSy5HcmFwZXRyZWUuSElWIDwtIGdnZHJhdygpICsgZHJhd19pbWFnZShUUEEuVUsuR3JhcGV0cmVlLkhJVi5maWxlKQpUUEEuVUsuR3JhcGV0cmVlLkhJVgoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksICJTdXBGaWc1X1RQQS1QSEVfR3JhcGV0cmVlLUhJVi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xODUsIGhlaWdodD0xMTAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKCgoKXApcCiMjIyBQaHlsb2dlbmV0aWMgY29udGV4dCBhbmFseXNlcwpcCk9rLCBub3cgbGV0cyBsb29rIGF0IHNvbWUgdHJlZXMKXApGaXJzdCwgbGV0J3MgZm9ybWFsaXNlIEJFQVNUIHRyZWUgcGxvdHRpbmcgYXMgdGhyZWUgc2VwYXJhdGUgZnVuY3Rpb25zIHRvIGVuYWJsZSBvdGhlciB0cmVlcyB0byBiZSBwbG90dGVkIHRoZSBzYW1lIHdheQpcCmBgYHtyfQpmdWxsLmJlYXN0Mi50cmVlIDwtIHJlYWQuYmVhc3QoZnVsbC5iZWFzdDIudHJlZS5maWxlKQpmdWxsLmJlYXN0Mi50cmVlQHBoeWxvJHRpcC5sYWJlbCA8LSBnc3ViKCJcXHwuKyQiLCIiLGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8kdGlwLmxhYmVsLCBwZXJsPVQpCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBmdW5jdGlvbiB0byBleHRyYWN0IGEgdHJlZSBiYXNlZCBvbiBzdWJsaW5lYWdlCkV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90IDwtIGZ1bmN0aW9uKG15LmJlYXN0LnRyZWUsIG15Lm1ldGFkYXRhLCBteS5waGUubWV0YSwgbXkuc3VibGluZWFnZSl7CiAgIyBnZXQgYWxsIHRpcHMgdG8gaW5jbHVkZSBmcm9tIG1ldGFkYXRhLCB0aGVuIGNhbGN1bGF0ZSBNUkNBIGZyb20gdHJlZQogIHN1YmxpbmVhZ2UudGVzdC5tcmNhIDwtIGdldE1SQ0EobXkuYmVhc3QudHJlZUBwaHlsbywgYXMuY2hhcmFjdGVyKHVubGlzdChteS5tZXRhZGF0YVtteS5tZXRhZGF0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZT09bXkuc3VibGluZWFnZSwiU2FtcGxlX05hbWUiXSkpKQogICMjIyMjIwogIFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgPC0gdHJlZV9zdWJzZXQobXkuYmVhc3QudHJlZSwgbm9kZT1zdWJsaW5lYWdlLnRlc3QubXJjYSwgbGV2ZWxzX2JhY2s9MCkKICByZXR1cm4oVFBBLmJlYXN0LnN1YnRyZWUudGVzdCkKfQojRXh0cmFjdF9zdWJsaW5lYWdlX3RyZWVfZm9yX3Bsb3QoZnVsbC5iZWFzdDIudHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIG15LnN1YmxpbmVhZ2UgPSAxKQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCgojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBGdW5jdGlvbiB0byBwcmVwYXJlIGEgYmVhc3QgdHJlZSB3aXRoIHRpbWVzY2FsZSBpbmRpY2F0b3JzLCBwb3N0ZXJpb3Igc3VwcG9ydCBhbmQgOTUlIEhQRCBiYXJzCnBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRCA8LSBmdW5jdGlvbihteS5iZWFzdC50cmVlLCBteS5tZXRhZGF0YSwgbXkucGhlLm1ldGEsIG1yc2QuZnVsbHRyZWUpewogICMgZ2V0IE1SQ0QgZm9yIHRyZWUKICBtcnNkLkJlYXN0LnRyZWUudGVzdC5zIDwtIG1heChhcy5udW1lcmljKHVubGlzdChteS5tZXRhZGF0YVtteS5tZXRhZGF0YSRTYW1wbGVfTmFtZSAlaW4lIG15LmJlYXN0LnRyZWVAcGh5bG8kdGlwLmxhYmVsLCJTYW1wbGVfWWVhciJdKSkpCiAgbXJzZC5CZWFzdC50cmVlLnRlc3QgPC0gbHVicmlkYXRlOjp5bWQocGFzdGUwKG1yc2QuQmVhc3QudHJlZS50ZXN0LnMsIi0wNi0wMSIpKSAKICBtcnNkLkJlYXN0LnRyZWUuZnVsbHRyZWUgPC0gbHVicmlkYXRlOjp5bWQobXJzZC5mdWxsdHJlZSkgCiAgI21yc2QuQmVhc3QudHJlZS50ZXN0CiAgIyBwbG90IGJhc2ljIHRyZWUKICBvcHRpb25zKGlnbm9yZS5uZWdhdGl2ZS5lZGdlPVRSVUUpCiAgcC5UUEEuYmVhc3Quc3VidHJlZS50ZXN0IDwtIGdndHJlZShteS5iZWFzdC50cmVlLCBtcnNkPW1yc2QuQmVhc3QudHJlZS50ZXN0LCBsYWRkZXJpemUgPSBULCBzaXplPTAuNCkgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxOTYwLDIwMjAsMTApLCBtaW5vcl9icmVha3M9c2VxKDIwMDAsIDIwMjAsIDEpKSArCiAgICB0aGVtZV90cmVlMigpICsKICAgICMgQWRkIGRhdGUgbGluZXMgZm9yIGVhc3kgaW50ZXJwcmV0YXRpb24gIAogICAgdGhlbWUocGFuZWwuZ3JpZC5tYWpvciAgID0gZWxlbWVudF9saW5lKGNvbG9yPSJncmV5NTAiLCBzaXplPS4yKSwKICAgICAgICAgIHBhbmVsLmdyaWQubWlub3IgICA9IGVsZW1lbnRfbGluZShjb2xvcj0iZ3JleTg1Iiwgc2l6ZT0uMiksCiAgICAgICAgICBwYW5lbC5ncmlkLm1ham9yLnkgPSBlbGVtZW50X2JsYW5rKCksCiAgICAgICAgICBwYW5lbC5ncmlkLm1pbm9yLnkgPSBlbGVtZW50X2JsYW5rKCkpCiAgIyBBZGQgcG9zdGVyaW9yIHN1cHBvcnQgYXMgbm9kZSBwb2ludHMKICBwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgPC0gcC5UUEEuYmVhc3Quc3VidHJlZS50ZXN0ICsgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPjAuOCkpLGNvbG9yPSJncmF5NjAiLHNpemU9MixhbHBoYT0wLjUsIHNoYXBlPTE4KSArIAogICAgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPjAuOTEpKSxjb2xvcj0iZ3JheTQwIixzaXplPTMsc2hhcGU9MTgsYWxwaGE9MC41KSArIAogICAgZ2VvbV9wb2ludDIoYWVzKHN1YnNldD0oIWlzVGlwICYgYXMubnVtZXJpYyhwb3N0ZXJpb3IpPj0wLjk2KSksY29sb3I9ImJsYWNrIixzaXplPTMsc2hhcGU9MTgsYWxwaGE9MC41KQogICMjIyMjIwogICMgZXh0cmFjdCA5NSUgSFBEIGludGVydmFscyAtIGdlb21fcmFuZ2Ugc2VlbXMgdW5hYmxlIHRvIGRvIGNvcnJlY3RseSB3aXRoIHRoaXMgdHJlZSAoa25vd24gYnVnIGZvciB0aXAgZGF0ZWQgdHJlZXMpLCBzbyBleHRyYWN0IGRhdGEgYW5kIHBsb3QgdXNpbmcgZ2VvbV9zZWdtZW50CiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhIDwtIGZvcnRpZnkobXkuYmVhc3QudHJlZSkKICBtaW5tYXggPC0gdChtYXRyaXgodW5saXN0KFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZGF0YVshaXMubmEoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhJGhlaWdodF8wLjk1X0hQRCksImhlaWdodF8wLjk1X0hQRCJdKSxucm93PTIpKQogIGJhcl9kZiA8LSBkYXRhLmZyYW1lKG5vZGVfaWQ9VFBBLmJlYXN0LnN1YnRyZWUudGVzdC5kYXRhWyFpcy5uYShUUEEuYmVhc3Quc3VidHJlZS50ZXN0LmRhdGEkaGVpZ2h0XzAuOTVfSFBEKSwibm9kZSJdLGFzLmRhdGEuZnJhbWUobWlubWF4KSkKICBuYW1lcyhiYXJfZGYpIDwtIGMoJ25vZGVfaWQnLCdtaW4nLCdtYXgnKSAKICBiYXJfZGYgPC0gYmFyX2RmICU+JSBmaWx0ZXIobm9kZV9pZCA+IE50aXAobXkuYmVhc3QudHJlZUBwaHlsbykpCiAgYmFyX2RmIDwtIGJhcl9kZiAlPiUgbGVmdF9qb2luKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZGF0YSwgYnk9Yygnbm9kZV9pZCc9J25vZGUnKSkgIyU+JSBzZWxlY3Qobm9kZV9pZCxtaW4sbWF4LHkpCiAgI21yY2QuZGVjaW1hbCA8LSBkZWNpbWFsX2RhdGUobXJzZC5CZWFzdC50cmVlLnRlc3QpCiAgbXJjZC5kZWNpbWFsIDwtIGRlY2ltYWxfZGF0ZShtcnNkLkJlYXN0LnRyZWUuZnVsbHRyZWUpCiAgCiAgIyBOb3cgYWRkIEhQRHMgdG8gcGxvdAogIHAuVFBBLmJlYXN0LnN1YnRyZWUudGVzdCA8LSBwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QgKyBnZW9tX3NlZ21lbnQoYWVzKHg9bXJjZC5kZWNpbWFsLW1heCwgeT15LCB4ZW5kPW1yY2QuZGVjaW1hbC1taW4sIHllbmQ9eSksIGRhdGE9YmFyX2RmLCBjb2xvcj0ncmVkJywgYWxwaGE9MC4yLCBzaXplPTIuMCkKICAjIE91dHB1dCB0cmVlIAogIHJldHVybihwLlRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QpCn0KIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCgoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjCiMgRnVuY3Rpb24gdG8gYWRkIG1ldGFkYXRhIHRvIHRyZWUKIyBIYXMgdHdvIG9wdGlvbmFsIGFyZ3VtZW50cyAiaW5pdGlhbC50cmFjay5vZmZzZXQiIGFuZCAidHJhY2suc2NhbGluZyIgd2hpY2ggY2FuIGJlIHVzZWQgdG8gYWx0ZXIgdGhlIHdpZHRoIGFuZCBwb3NpdGlvbmluZyBvZiBtZXRhZGF0YSB0cmFja3MKCnBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YSA8LSBmdW5jdGlvbihteS5iZWFzdC50cmVlLmlucHV0LCBteS5tZXRhZGF0YSwgbXkucGhlLm1ldGEsIGluaXRpYWwudHJhY2sub2Zmc2V0LCB0cmFjay5zY2FsaW5nKXsKICAgICMgQWRkIGNvZGUgdG8gYWxsb3cgc2NhbGluZyB1cCBvZiB0aGUgdHJhY2sgb2Zmc2V0cyBhbmQgd2lkdGhzIC0gdXNlZnVsIGZvciBtdWNoIGJpZ2dlciBsZW5ndGggdHJlZXMKICBpZihtaXNzaW5nKGluaXRpYWwudHJhY2sub2Zmc2V0KSl7CiAgICBpbml0aWFsLnRyYWNrLm9mZnNldCA8LSAwCiAgfSAgICAKICBpZihtaXNzaW5nKHRyYWNrLnNjYWxpbmcpKXsKICAgIHRyYWNrLnNjYWxpbmcgPC0gMQogIH0KICAjIENhbGN1bGF0ZSBhbW91bnQgdG8gb2Zmc2V0IGVhY2ggaGVhdG1hcCB0cmFjawogIG9mZnNldC5kaXN0IDwtIDQqdHJhY2suc2NhbGluZwogIHRyYWNrLndpZHRoIDwtICgxL21heChteS5iZWFzdC50cmVlLmlucHV0JGRhdGEkaGVpZ2h0KSozKSp0cmFjay5zY2FsaW5nCiAgCiAgIyBtYWtlIGEgbGlzdCBvZiB0YXhhIHVzZWQgaW4gdGhpcyBwbG90IAogIG15LnRheGEubGlzdCA8LSBhcy5jaGFyYWN0ZXIodW5saXN0KGZpbHRlcihteS5iZWFzdC50cmVlLmlucHV0JGRhdGEsIGlzVGlwPT1UUlVFKSAlPiUgc2VsZWN0KGxhYmVsKSkpCiAgCiAgIyBtYWtlIGEgY29sb3Igc2NhbGUgZm9yIHNhbXBsaW5nIHllYXJzCiAgI1BIRS5zdWJsaW50ZXN0LnllYXIuY29scyA8LSBkYXRhLmZyYW1lKHllYXI9c29ydCh1bmlxdWUoYXMubnVtZXJpYyh1bmxpc3QobXkubWV0YWRhdGFbKG15Lm1ldGFkYXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0KSwiU2FtcGxlX1llYXIiXSx1c2UubmFtZXM9RikpKSksc3RyaW5nc0FzRmFjdG9ycyA9IFQpCiAgI1BIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLmNvbHMgPC0gY29sb3JSYW1wUGFsZXR0ZShicmV3ZXIucGFsKDcsICJZbE9yUmQiKSkobnJvdyhQSEUuc3VibGludGVzdC55ZWFyLmNvbHMpKQogIAogICMgT3IgYWx0ZXJuYXRpdmVseSwgdXNlIGEgY29tbW9uIGNvbG91ciBzY2hlbWUgZm9yIGFsbCBkYXRhIChtYXliZSBtb3JlIHNlbnNpYmxlKQogIFBIRS5zdWJsaW50ZXN0LnllYXIuY29scyA8LSBkYXRhLmZyYW1lKHllYXI9VFBBLnllYXIuY3V0dG9mZi5jb2xzJGRhdGUuY3V0dG9mZiwgeWVhci5jb2xzPVRQQS55ZWFyLmN1dHRvZmYuY29scyRkYXRlLmN1dHRvZmYuY29sLCBzdHJpbmdzQXNGYWN0b3JzID0gRikKICAKICAjIG1ha2UgbWV0YWRhdGEgZmlsZSBmb3IgVUsgcmVnaW9ucyBwcmVzZW50IGluIHN1YmxpbmVhZ2UKICBzdWJsaW4udGVzdC5yZWdpb24ubWV0YSA8LSBkYXRhLmZyYW1lKHJvdy5uYW1lcz1hcy5jaGFyYWN0ZXIodW5saXN0KG15LnBoZS5tZXRhW215LnBoZS5tZXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0LCJTYW1wbGVfTmFtZSJdKSksIFJlZ2lvbj1hcy5jaGFyYWN0ZXIodW5saXN0KG15LnBoZS5tZXRhW215LnBoZS5tZXRhJFNhbXBsZV9OYW1lICVpbiUgbXkudGF4YS5saXN0LCJwaGVfY2VudHJlIl0pKSwgc3RyaW5nc0FzRmFjdG9ycyA9IEYpCiAgCiAgIyBBZGQgaGVhdG1hcCBzdHJpcHMKICAjIFNhbXBsZSBZZWFyCiAgI1RQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsIDwtIGdoZWF0bWFwKG15LmJlYXN0LnRyZWUuaW5wdXQsIFRQQS5yYXdzZXEuYWxsLlllYXJzLnAsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsIG9mZnNldD1pbml0aWFsLnRyYWNrLm9mZnNldCtvZmZzZXQuZGlzdCxjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsKICAgICNzY2FsZV9maWxsX21hbnVhbChuYW1lPSJZZWFyIiwgdmFsdWVzPVBIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLmNvbHMsYnJlYWtzPVBIRS5zdWJsaW50ZXN0LnllYXIuY29scyR5ZWFyLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEsIG5jb2w9MikpICsKICAgICNnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gZ2hlYXRtYXAobXkuYmVhc3QudHJlZS5pbnB1dCwgVFBBLnJhd3NlcS55ZWFyLmN1dHRvZmYucCwgY29sb3I9TlVMTCx3aWR0aD10cmFjay53aWR0aCwgb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0K29mZnNldC5kaXN0LGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKwogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iWWVhciIsIHZhbHVlcz1QSEUuc3VibGludGVzdC55ZWFyLmNvbHMkeWVhci5jb2xzLGJyZWFrcz1QSEUuc3VibGludGVzdC55ZWFyLmNvbHMkeWVhciwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxLCBuY29sPTIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgCiAgIyBBZGQgY291bnRyeQogIFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsIDwtIGdoZWF0bWFwKFRQQS5iZWFzdC5zdWJ0cmVlLnRlc3QuZ2xvYmFsLnBsb3QxLnJlZ2lvbmFsLCBUUEEucmF3c2VxLmNvdW50cmllcy5wLCBjb2xvcj1OVUxMLHdpZHRoPXRyYWNrLndpZHRoLCBvZmZzZXQ9aW5pdGlhbC50cmFjay5vZmZzZXQrKG9mZnNldC5kaXN0KjIpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkNvdW50cnkiLCB2YWx1ZXM9Y29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJGNvdW50cnkuY29sLCBicmVha3M9Y29udGluZW50YWwuY291bnRyeS5jb2xzLmJyZXcyJEdlb19Db3VudHJ5LCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCiAgIyBVSyBvciBub24tVUsKICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBnaGVhdG1hcChUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBUUEEucmF3c2VxLlVLLnAsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0KyhvZmZzZXQuZGlzdCozKSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJFbmdsYW5kL090aGVyIiwgYnJlYWtzPWMoIkVuZ2xhbmQiLCJPdGhlciIpLCB2YWx1ZXM9YygiYmxhY2siLCJncmV5OTUiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDMsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKICAjIFVLIFBIRSByZWdpb24KICBUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCA8LSBnaGVhdG1hcChUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCwgc3VibGluLnRlc3QucmVnaW9uLm1ldGEsIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsIG9mZnNldD1pbml0aWFsLnRyYWNrLm9mZnNldCsob2Zmc2V0LmRpc3QqNCksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iVUtIU0EgUmVnaW9uIiwgdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24sIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA0KSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQogIAogICMgVFBBIHN1YmxpbmVhZ2UKICAjVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gZ2hlYXRtYXAoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVRQQS5tZXRhMi4xJFNhbXBsZV9OYW1lLCBTdWJsaW5lYWdlPVRQQS5tZXRhMi4xJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBzdHJpbmdzQXNGYWN0b3JzID0gRiksIGNvbG9yPU5VTEwsd2lkdGg9dHJhY2sud2lkdGgsb2Zmc2V0PWluaXRpYWwudHJhY2sub2Zmc2V0KyhvZmZzZXQuZGlzdCo1KSwgY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsZm9udC5zaXplPTIuNSkgKyAKICAjc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iU3VibGluZWFnZSIsdmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA1KSkgCiAgCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgKyB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogICAgbmV3X3NjYWxlX2ZpbGwoKSArCiAgICBnZW9tX3Jvb3RlZGdlKDIpICsKICAgIE5VTEwKICAKICAjIGNhbGN1bGF0ZSBudW1iZXIgb2YgdGF4YQogIHRlc3QudGF4YWNvdW50IDwtIGxlbmd0aChteS50YXhhLmxpc3QpCiAgIyBBZGp1c3QgZmluYWwgcGxvdCB4IGFuZCB5IGF4aXMgdG8gbWFrZSBzcGFjZSBmb3IgbGFiZWxzIHVzaW5nIHRheGEgY291bnRzCiAgeC5heGlzLmxpbWl0cyA8LSBnZ3Bsb3RfYnVpbGQoVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwpJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlCiAgVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgPC0gVFBBLmJlYXN0LnN1YnRyZWUudGVzdC5nbG9iYWwucGxvdDEucmVnaW9uYWwgKyAKICAgIGNvb3JkX2NhcnRlc2lhbih5PWMoLTAuNS0odGVzdC50YXhhY291bnQvMTUpLHRlc3QudGF4YWNvdW50KzIpLCB4PWMoeC5heGlzLmxpbWl0c1sxXSx4LmF4aXMubGltaXRzWzJdKzMpKQogIAogIHJldHVybihUUEEuYmVhc3Quc3VidHJlZS50ZXN0Lmdsb2JhbC5wbG90MS5yZWdpb25hbCkKfQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKCmBgYAoKXApHcmVhdCwgbm93IGxldCdzIHBsb3QgYSBmdWxsIGJlYXN0IHRyZWUKYGBge3IsIGZpZy5oZWlnaHQ9MTAsIGZpZy53aWR0aD0xMH0KIyBmdW5jdGlvbiBmb3IgeC1heGlzIHRpbWUgYnJlYWtzIG5lZWRzIHR3ZWFraW5nIGZvciB0aGUgZnVsbCB0cmVlClRQQS5HbG9iYWwuZnVsbC5CZWFzdFRyZWUudWttZXRhIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQobXkuYmVhc3QudHJlZSA9IGZ1bGwuYmVhc3QyLnRyZWUsIG15Lm1ldGFkYXRhID0gVFBBLm1ldGEyLjEsIG15LnBoZS5tZXRhID0gUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXJzZC5mdWxsdHJlZSA9ICIyMDE5LTA2LTAxIikgKyBzY2FsZV94X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgxNDAwLDIwMjAsNTApLCBtaW5vcl9icmVha3M9c2VxKDE5NTAsIDIwMjAsIDUpKSwgbXkubWV0YWRhdGEgPSBUUEEubWV0YTIuMSwgbXkucGhlLm1ldGEgPSBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gNSkKClRQQS5HbG9iYWwuZnVsbC5CZWFzdFRyZWUudWttZXRhCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiU3VwRmlnN19UUEFfRnVsbEJlYXN0VHJlZS4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xODUsIGhlaWdodD0yNDAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCmBgYAoKXApOb3cgZG8gc3VibGluZWFnZSBwbG90cwpcCk1ha2Ugc29tZSBwbG90cwpgYGB7ciwgd2FybmluZz1GQUxTRX0KIyBTdWJsaW5lYWdlIDEKc3VibGluZWFnZS4xLnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMSksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gMS4yKQoKIyBTdWJsaW5lYWdlLjIKc3VibGluZWFnZS4yLnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCB0cmFjay5zY2FsaW5nID0gMSkKCiMgU3VibGluZWFnZS44CnN1YmxpbmVhZ2UuOC50cmVlLmhlYXRtYXAgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDgpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgdHJhY2suc2NhbGluZyA9IDEuMSkKCiMgU3VibGluZWFnZS4xNApzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcCA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMTQpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgdHJhY2suc2NhbGluZyA9IDEuMSkKCmBgYAoKXApQbG90IHRvZ2V0aGVyPwpcCk1heWJlIHdpdGggc3VibGluZWFnZSAxIGV4cGFuZGVkPwpgYGB7ciwgZmlnLndpZHRoPTEyLCBmaWcuaGVpZ2h0PTEyfQpwLmJlYXN0LnRyZWVzLmhlYXRtYXAuc3VibGluZWFnZXMuY29tYmkub2Zmc2V0MSA8LSBwbG90X2dyaWQoc3VibGluZWFnZS4yLnRyZWUuaGVhdG1hcCwgCiAgICAgICAgICBzdWJsaW5lYWdlLjgudHJlZS5oZWF0bWFwLCAKICAgICAgICAgIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLCAKICAgICAgICAgIG5jb2w9MiwgbGFiZWxzPWMoIkIgLSBTdWJsaW5lYWdlIDIiLCJDIC0gU3VibGluZWFnZSA4IiwiRCAtIFN1YmxpbmVhZ2UgMTQiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NSwgdmp1c3Q9MS4wKQoKcC5iZWFzdC50cmVlcy5oZWF0bWFwLnN1YmxpbmVhZ2VzLmNvbWJpLm9mZnNldDIgPC0gcGxvdF9ncmlkKHN1YmxpbmVhZ2UuMS50cmVlLmhlYXRtYXAsIHAuYmVhc3QudHJlZXMuaGVhdG1hcC5zdWJsaW5lYWdlcy5jb21iaS5vZmZzZXQxLCBsYWJlbHM9YygiQSAtIFN1YmxpbmVhZ2UgMSIsICIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NzUsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDYsMTEpLCB2anVzdD0yLjUpCgoKcC5iZWFzdC50cmVlcy5oZWF0bWFwLnN1YmxpbmVhZ2VzLmNvbWJpLm9mZnNldDIKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzhfVFBBLVBIRV9TdWJsaW5lYWdlLUJlYXN0VHJlZXMuIixmb3JtYXQoU3lzLkRhdGUoKSwiJVklbSVkIiksIi5wZGYiKSwgdW5pdHM9J21tJywgd2lkdGg9MjY1LCBoZWlnaHQ9MjMwLCBkZXZpY2U9J3BkZicsIGRwaT0xMjAwKQpgYGAKClwKTmVlZCB0byBleHBsb3JlIHN1YmxpbmVhZ2UgMTQgYSBiaXQgbW9yZSB0byBnZXQgZGF0ZXMgZm9yIHRob3NlIHN1YmNsYWRlcwpgYGB7cn0Kc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGxpbmVzaXplPTAuNCkgIzMKYGBgCgpcCmBgYHtyfQojIE9rLCB0aGVyZSBhcmUgbXVsdGlwbGUgc3ViY2xhZGVzIGluIHRoaXMgdHJlZQpzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcC5kYXRhIDwtIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwJGRhdGEKCiMgZ2V0TVJDQShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLGMoIlBIRTE1MDE1MEEiLCJOTDE0IiwiVFBBX0JDQzEyMiIsIlRQQV9CQ0MxMjYiLCJQSEUxNDAwNzZBIiwiVFBBX1VLQlJHMDA4IikpICA5ODIKIyBmdWxsLmJlYXN0Mi50cmVlQHBoeWxvJHRpcC5sYWJlbFtwaGFuZ29ybjo6RGVzY2VuZGFudHMoZnVsbC5iZWFzdDIudHJlZUBwaHlsbywgOTgyLCB0eXBlID0gYygidGlwcyIpKVtbMV1dXQoKc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QgPC0gYygiTkwxNyIsICJOTDE5IiwgIlBIRTE0MDA4NUEiLCAiUEhFMTQwMDg5QSIsICJQSEUxNTAxMThBIiwgIlBIRTE1MDEyMUEiLCAiUEhFMTUwMTMzQSIsICJQSEUxNTAxNDNBIiwgIlBIRTE1MDE0NUEiLCAiUEhFMTUwMTYyQSIsICJQSEUxNTAxNjZBIiwgIlBIRTE1MDE2OEEiLCAiUEhFMTYwMjI0QSIsICJQSEUxNjAyNDNBIiwgIlBIRTE2MDI1NUEiLCAiUEhFMTYwMjc2QSIsICJQSEUxNjAyOTBBIiwgIlBIRTE2MDMwMkEiLCAiUEhFMTYwMzA2QSIsICJQSEUxNzAzMzNBIiwgIlBIRTE3MDM0OUEiLCAiUEhFMTcwMzc0QSIsICJQSEUxNzAzODFBIiwgIlBIRTE3MDY2NEEiLCAiVFBBX0VTQkNOMDA1IiwgIlRQQV9VS0JJUjAzMiIpCgpzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdCA8LSBjKCJOTDE0IiwgIlBIRTE0MDA3NkEiLCAiUEhFMTUwMTQ5QSIsICJQSEUxNTAxNTBBIiwgIlBIRTE1MDE3MEEiLCAiUEhFMTYwMTk2QSIsICJQSEUxNjAyNjNBIiwgIlBIRTE2MDI3NEEiLCAiUEhFMTYwMjg3QSIsICJQSEUxNjAyOTRBIiwgIlBIRTE2MDMxNkEiLCAiUEhFMTYwMzE3QSIsICJQSEUxNzAzNzJBIiwgIlBIRTE3MDM4NkEiLCAiUEhFMTcwMzk3QSIsICJQSEUxNzA0MDVBIiwgIlRQQV9CQ0MwODEiLCAiVFBBX0JDQzA4OCIsICJUUEFfQkNDMDg5IiwgIlRQQV9CQ0MxMDEiLCAiVFBBX0JDQzEyMiIsICJUUEFfQkNDMTI2IiwgIlRQQV9CQ0MxMzYiLCAiVFBBX0JDQzE2OSIsICJUUEFfSFVOMTgwMDA0IiwgIlRQQV9IVU4xOTAwMjAiLCAiVFBBX1VLQklSMDQ0IiwgIlRQQV9VS0JSRzAwNyIsICJUUEFfVUtCUkcwMDgiKQoKIyBHZXQgTVJDQSBkYXRlIGZvciBsb3dlciBjbGFkZQpzdWJsaW5lYWdlLjE0Lmxvd2VyY2xhZGUubGlzdC50bXJjYSA8LSBzdWJsaW5lYWdlLjE0LnRyZWUuaGVhdG1hcC5kYXRhW3N1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLmRhdGEkbm9kZT09Z2V0TVJDQShFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDE0KUBwaHlsbywgc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QpLCJ4Il0KCnBhc3RlMCgiVE1SQ0EgZm9yIHN1YmxpbmVhZ2UgMTQgbG93ZXIgY2xhZGU6ICIsc3VibGluZWFnZS4xNC5sb3dlcmNsYWRlLmxpc3QudG1yY2EpCgojIEdldCBNUkNBIGRhdGUgZm9yIHVwcGVyIGNsYWRlCnN1YmxpbmVhZ2UuMTQudXBwZXJjbGFkZS5saXN0LnRtcmNhIDwtIHN1YmxpbmVhZ2UuMTQudHJlZS5oZWF0bWFwLmRhdGFbc3VibGluZWFnZS4xNC50cmVlLmhlYXRtYXAuZGF0YSRub2RlPT1nZXRNUkNBKEV4dHJhY3Rfc3VibGluZWFnZV90cmVlX2Zvcl9wbG90KGZ1bGwuYmVhc3QyLnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBteS5zdWJsaW5lYWdlID0gMTQpQHBoeWxvLCBzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdCksIngiXQoKcGFzdGUwKCJUTVJDQSBmb3Igc3VibGluZWFnZSAxNCB1cHBlciBjbGFkZTogIixzdWJsaW5lYWdlLjE0LnVwcGVyY2xhZGUubGlzdC50bXJjYSkKYGBgClwKXApFeHRyYWN0IGtleSBpbmZvcm1hdGlvbiBmb3Igc3VibGluZWFnZSA2ICh0d28gc2FtcGxlcykKYGBge3J9CnN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDYpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCkKCnN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAuZGF0YSA8LSBzdWJsaW5lYWdlLjYudHJlZS5oZWF0bWFwJGRhdGEKCiMgR2V0IE1SQ0EgZGF0ZSBmb3IgdXBwZXIgY2xhZGUKc3VibGluZWFnZS42LmJlYXN0dHJlZS50bXJjYSA8LSBhcy5udW1lcmljKHN1YmxpbmVhZ2UuNi50cmVlLmhlYXRtYXAuZGF0YVtzdWJsaW5lYWdlLjYudHJlZS5oZWF0bWFwLmRhdGEkbm9kZT09Z2V0TVJDQShFeHRyYWN0X3N1YmxpbmVhZ2VfdHJlZV9mb3JfcGxvdChmdWxsLmJlYXN0Mi50cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgbXkuc3VibGluZWFnZSA9IDYpQHBoeWxvLCBjKCJQSEUxMzAwNDhBIiwgIlBIRTE2MDI4M0EiKSksImJyYW5jaCJdKQoKCnBhc3RlMCgiVE1SQ0EgZm9yIHN1YmxpbmVhZ2UgNiB1cHBlciBjbGFkZTogIixzdWJsaW5lYWdlLjYuYmVhc3R0cmVlLnRtcmNhKQpgYGAKCgoKXApcCiMjIyBFeHRyYWN0IHNhbXBsZSAmIHBvcHVsYXRpb24gc3RhdGlzdGljcyBmcm9tIGRhdGFzZXRzIGZvciB1c2UgaW4gbWFudXNjcmlwdCB0ZXh0ClwKRGF0YXNldCBhbmQgR2VvZ3JhcGhpY2FsIGRpc3RyaWJ1dGlvbnMKYGBge3J9CiMgZGF0YXNldCBjb3VudHMKcGFzdGUwKCJUb3RhbCBVSyBzYW1wbGVzIGluIGNsZWFuZWQvZGVkdXBsaWNhdGVkIGRhdGFzZXQ6ICIsbnJvdyhQSEUubWV0YWRhdGEubGlua2VkKSkKcGFzdGUwKCJPZiB3aGljaDogIixucm93KFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRpcy5QSEU9PSJQSEUiLF0pLCIgZnJvbSBQSEUgUmVmIGxhYiBhdCBDb2xpbmRhbGUiKQpwYXN0ZTAoIk9mIHdoaWNoOiAiLG5yb3coUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJGlzLlBIRT09Ik90aGVyIixdKSwiIGZyb20gb3RoZXIgbGFicyIpCgojIHByb3BvcnRpb24gd2l0aCBnZW9ncmFwaGljYWwgZGF0YQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkWyhQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmUgJW5vdGluJSBjKCJOb3QgS25vd24iLCJVSyAobm90IEVuZ2xhbmQpIikpLF0pLCIgd2VyZSBncm91cGVkIGludG8gb25lIG9mIHRoZSA5IFBIIHJlZ2lvbnMiKQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZT09IlVLIChub3QgRW5nbGFuZCkiLF0pLCAiIHdlcmUgcmVmZXJyZWQgZnJvbSBvdXRzaWRlIEVuZ2xhbmQiKQpwYXN0ZTAoIkZyb20gVUsgc2FtcGxlcywgIiwgbnJvdyhQSEUubWV0YWRhdGEubGlua2VkW1BIRS5tZXRhZGF0YS5saW5rZWQkcGhlX2NlbnRyZT09Ik5vdCBLbm93biIsXSksICIgaGFkIHVua25vd24gcmVnaW9uIikKCiMgY291bnRzICYgZnJhY3Rpb25zIGJ5IFBIRSByZWdpb24KUEhFLmdlby5jb3VudAoKYGBgClwKR2VuZGVyIE9yaWVudGF0aW9uIHN0YXRzCmBgYHtyfQpQSEUub3JpZW50YXRpb24uY291bnRzClBIRS5nZW8ub3JpZW50YXRpb24uY291bnRzClBIRS5nZW8uSElWLmNvdW50cwpQSEUuc3VibGluZWFnZS5vcmllbnRhdGlvbi5jb3VudHMKUEhFLnN1YmxpbmVhZ2UuQWdlCmBgYAoKXApTdWJsaW5lYWdlIERpc3RyaWJ1dGlvbnMKYGBge3J9ClBIRS5MaW5lYWdlLmNvdW50ClBIRS5zdWJsaW4uY291bnQKUEhFLmdlby5zdWJsaW5lYWdlCmBgYAoKXApNYWNyb2xpZGUgcmVzaXN0YW5jZSBzdGF0cwpgYGB7cn0KVUsubWFjcm9saWRlLnJlcyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JQogIGRwbHlyOjpncm91cF9ieShBMjA1OEcsIEEyMDU5RykgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudC5hbGxlbGU9bigpKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuY291bnQ9c3VtKENvdW50LmFsbGVsZSksIHBlcmMuYWxsZWxlPXJvdW5kKChDb3VudC5hbGxlbGUvdG90YWwuY291bnQpKjEwMCwxKSkKVUsubWFjcm9saWRlLnJlcwoKVUsubWFjcm9saWRlLnJlcy5zdWJsaW4gPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIEEyMDU4RywgQTIwNTlHKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LmFsbGVsZT1uKCkpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUKICBkcGx5cjo6bXV0YXRlKHRvdGFsLmNvdW50PXN1bShDb3VudC5hbGxlbGUpLCBwZXJjLmFsbGVsZT1yb3VuZCgoQ291bnQuYWxsZWxlL3RvdGFsLmNvdW50KSoxMDAsMSkpClVLLm1hY3JvbGlkZS5yZXMuc3VibGluCgoKIyBDYWxjdWxhdGUgbG9uZyBmb3JtIGRmLCB3aXRoIGRpZmZlcmVudCAyM1MgYWxsZWxlcyAoQTIwNThHLCBBMjA1OUcsIFdULCBVbmNlcnRhaW4pIHYucy4gc3VibGluZWFnZQpVSy5tYWNyb2xpZGUucmVzLnN1Ymxpbi5sb25nIDwtIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lCiAgbXV0YXRlKFJlc2lzdGFuY2UuYWxsZWxlPWlmZWxzZShBMjA1OEc9PSJZZXMiLCAiQTIwNThHIiwgaWZlbHNlKEEyMDU5Rz09IlllcyIsICJBMjA1OUciLCBpZmVsc2UoKEEyMDU4Rz09Ik5vIiAmIEEyMDU5Rz09Ik5vIiksIldpbGQgVHlwZSIsICJVbmNlcnRhaW4iKSkpKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIFJlc2lzdGFuY2UuYWxsZWxlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LnBlci5zdWJsaW4uTWFjcm9saWRlcz1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudC5wZXIuc3VibGluLk1hY3JvbGlkZXMpLCAKICAgICAgICAgICAgICAgIGZyYWN0aW9uPUNvdW50LnBlci5zdWJsaW4uTWFjcm9saWRlcy90b3RhbC5zdWJsaW4pICU+JQogICNkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjphcnJhbmdlKChSZXNpc3RhbmNlLmFsbGVsZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGN1bV9mcmFjdCA9IGN1bXN1bShmcmFjdGlvbikpICU+JQogIGRwbHlyOjptdXRhdGUoY3VtX2ZyYWN0Lm1pZCA9IGN1bV9mcmFjdC0oZnJhY3Rpb24vMikpICU+JQogIGRwbHlyOjptdXRhdGUoUmVzaXN0YW5jZS5hbGxlbGUgPSBmYWN0b3IoUmVzaXN0YW5jZS5hbGxlbGUsIGxldmVscz1yZXYoYygiQTIwNThHIiwgIkEyMDU5RyIsICJVbmNlcnRhaW4iLCAiV2lsZCBUeXBlIikpKSkKCiMgTWFrZSBwbG90IG9mIG1hY3JvbGlkZSByZXNpc3RhbmNlIGJ5IHN1YmxpbmVhZ2VzCnAuc3VibGluLk1hY3JvbGlkZXMuaGJhcnBsb3QgPC0gZ2dwbG90KFVLLm1hY3JvbGlkZS5yZXMuc3VibGluLmxvbmcsIGFlcyhDb3VudC5wZXIuc3VibGluLk1hY3JvbGlkZXMsIHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGZpbGw9UmVzaXN0YW5jZS5hbGxlbGUpKSArCiAgZ2VvbV9iYXJoKHN0YXQ9ImlkZW50aXR5IiwgcG9zaXRpb249ImZpbGwiLCB3aWR0aD0wLjY1KSArCiAgdGhlbWVfbGlnaHQoKSArCiAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTWFjcm9saWRlXG5SZXNpc3RhbmNlXG5BbGxlbGUiLHZhbHVlcz1jKCJpbmRpYW5yZWQyIiwgInN0ZWVsYmx1ZTEiLCJncmV5NTUiLCAiZ3JleTkwIiksIGJyZWFrcz1jKCJBMjA1OEciLCAiQTIwNTlHIiwgIlVuY2VydGFpbiIsICJXaWxkIFR5cGUiKSkgKwogIGxhYnMoeT0iVFBBIFN1YmxpbmVhZ2UiLCB4PSJQcm9wb3J0aW9uIHdpdGggTWFjcm9saWRlIFJlc2lzdGFuY2UgQWxsZWxlIikgKwogIHRoZW1lLnRleHQuc2l6ZSArIHRoZW1lKGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpLGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJykgKwogIGd1aWRlcyhmaWxsPWd1aWRlX2xlZ2VuZChuY29sPTIpKSArCiAgZ2VvbV90ZXh0KGRhdGE9VUsubWFjcm9saWRlLnJlcy5zdWJsaW4ubG9uZywgYWVzKGN1bV9mcmFjdC5taWQsIHk9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsbGFiZWw9Q291bnQucGVyLnN1Ymxpbi5NYWNyb2xpZGVzKSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBOVUxMCgpwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90CgoKIyBDb21iaW5lIHBsb3Qgd2l0aCBzdWJsaW5lYWdlIGNvdW50IGJhcnMKcC5zdWJsaW4uTWFjcm9saWRlcy5oYmFycGxvdC5jb21iaSA8LSBwbG90X2dyaWQocC5zdWJsaW5lYWdlLmhiYXJwbG90ICsgZ3VpZGVzKGZpbGw9Z3VpZGVfbGVnZW5kKG5jb2w9MykpLCBwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90ICsgeS50aGVtZS5zdHJpcCwgbnJvdz0xLCBhbGlnbj1ULCBsYWJlbHM9YygiQSIsICJCIiksIGxhYmVsX3NpemU9cGFuZWwubGFiLnNpemUpCgpwLnN1Ymxpbi5NYWNyb2xpZGVzLmhiYXJwbG90LmNvbWJpCgojZ2dzYXZlKHBhc3RlMChGaWd1cmVfb3V0cHV0X2RpcmVjdG9yeSwiU3VwRmlnOV9UUEEtUEhFX1N1Ymxpbi1NYWNyb2xpZGUtUmVzLiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTE2MCwgaGVpZ2h0PTEyMCwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCmBgYApcClwKCiMjIyBQYWlyd2lzZSBTTlAgYW5hbHlzaXMKXApPSywgd2FudCB0byBpbnZlc3RpZ2F0ZSB0aGUgZGlmZmVyZW50IHBhdHRlcm5zIG9ic2VydmFibGUgZm9yIHRoZSBOb3J0aCBFYXN0IG9mIEVuZ2xhbmQgKHBhbGUgYmx1ZSkgaW4gU3VibGluZWFnZSAxClwKTXVsdGlwbGUgd2F5cyB3ZSBjYW4gZG8gdGhpcyAtIGluY2x1ZGluZyBTTlAgZGlzdGFuY2VzIChhbHNvIG11bHRpcGxlIHdheXMgdG8gZG8gdGhhdCkKXApgYGB7cn0KIyMjCiNVc2UgcGh5bG9nZW5ldGljIGRpc3RhbmNlIGZyb20gdGhlIFNOUCBzY2FsZWQgdHJlZQpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdCA8LSBhcGU6OmNvcGhlbmV0aWMucGh5bG8oVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrKQpUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdC5tZWx0IDwtIGRhdGEuZnJhbWUoVGF4YTE9cm93Lm5hbWVzKFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0KSwgVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QsIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSAlPiUgdGlkeXI6OmdhdGhlcihUYXhhMiwgRGlzdGFuY2UuUGh5bG8sIC1UYXhhMSkKIyBUYXhhIENvbXBhcmlzb25zIGxhYmVsClRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQkVGF4YV9jb21iaW5hdGlvbiA8LSBzYXBwbHkoMTpucm93KFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQpLCBmdW5jdGlvbiAoeCkgcGFzdGUwKHNvcnQoYyhhcy5jaGFyYWN0ZXIoVFBBLnB5amFyLnRyZWUuc3Vic2V0LnVrLmNvcGhlbmV0aWMuU05QLmRpc3QubWVsdCRUYXhhMVt4XSksYXMuY2hhcmFjdGVyKFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQkVGF4YTJbeF0pKSksY29sbGFwc2U9Il9fXyIpKQojIE1lcmdlIHRvZ2V0aGVyCiNUUEEuV0dTLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdCA8LSBkcGx5cjo6bGVmdF9qb2luKFRQQS5XR1MuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0LCBUUEEucHlqYXIudHJlZS5zdWJzZXQudWsuY29waGVuZXRpYy5TTlAuZGlzdC5tZWx0WyxjKCJUYXhhX2NvbWJpbmF0aW9uIiwiRGlzdGFuY2UuUGh5bG8iKV0sIGJ5PSJUYXhhX2NvbWJpbmF0aW9uIikKClRQQS5XR1MuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0IDwtIFRQQS5weWphci50cmVlLnN1YnNldC51ay5jb3BoZW5ldGljLlNOUC5kaXN0Lm1lbHQKCgpUUEEuV0dTLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdCA8LSB1bmlxdWUoVFBBLldHUy5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQpCmBgYApcCk9rLCBub3cgYnJpbmcgaW4gc29tZSBtZXRhZGF0YSBhbmQgY29tcGFyaXNvbnMKYGBge3J9CiMgQnJpbmcgaW4gYW5kIG1lcmdlIG1ldGFkYXRhClBIRS5tZXRhLnBhaXJ3aXNlLnQxIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwieWVhciIsInBoZV9jZW50cmUiLCJsb25kb24iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsICJUUEFfTGluZWFnZSIsIkdlb19Db3VudHJ5IiwiaXMuVUsiLCJpcy5QSEUiLCAiU2FtcGxlX1llYXIiLCJkYXRlLmRlY2ltYWwiKV0KCmNvbG5hbWVzKFBIRS5tZXRhLnBhaXJ3aXNlLnQxKSA8LSBwYXN0ZTAoY29sbmFtZXMoUEhFLm1ldGEucGFpcndpc2UudDEpLCIudDEiKQpjb2xuYW1lcyhQSEUubWV0YS5wYWlyd2lzZS50MSlbMV0gPC0gIlRheGExIgpQSEUubWV0YS5wYWlyd2lzZS50MiA8LSBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJTYW1wbGVfTmFtZSIsInllYXIiLCJwaGVfY2VudHJlIiwibG9uZG9uIiwiZ2VuZGVyX29yaWVudGF0aW9uIiwiaGl2cG9zIiwiYWdlX2dyb3VwIiwidWtib3JuIiwiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiLCAiVFBBX0xpbmVhZ2UiLCJHZW9fQ291bnRyeSIsImlzLlVLIiwiaXMuUEhFIiwgIlNhbXBsZV9ZZWFyIiwiZGF0ZS5kZWNpbWFsIildCmNvbG5hbWVzKFBIRS5tZXRhLnBhaXJ3aXNlLnQyKSA8LSBwYXN0ZTAoY29sbmFtZXMoUEhFLm1ldGEucGFpcndpc2UudDIpLCIudDIiKQpjb2xuYW1lcyhQSEUubWV0YS5wYWlyd2lzZS50MilbMV0gPC0gIlRheGEyIgoKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIHBseXI6OmpvaW4oVFBBLldHUy5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQsUEhFLm1ldGEucGFpcndpc2UudDEsIGJ5PSJUYXhhMSIsIHR5cGU9ImxlZnQiKSAKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIHBseXI6OmpvaW4oUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLFBIRS5tZXRhLnBhaXJ3aXNlLnQyLCBieT0iVGF4YTIiLCB0eXBlPSJsZWZ0IikKCiMgRXhjbHVkZSBtaXNzaW5nIGRhdGEgKGUuZy4gbWlzc2luZyBzdWJsaW5lYWdlKSAtIHRoaXMgd2lsbCBhbHNvIHJlbW92ZSBub24tVUsgc2FtcGxlcywgc2luY2UgZnVsbCBtZXRhZGF0YSBpcyBtaXNzaW5nIGhlcmUKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVshaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQxKSxdClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbIWlzLm5hKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MiksXQoKYGBgCgpcCkRlZmluZSBjb21wYXJpc29ucwpgYGB7cn0KIyBTYW1lIHNhbXBsZQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5zYW1wbGUgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUYXhhMT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRheGEyLCJzYW1lIiwgImRpZmZlcmVudCIpCgojIFllYXJzIGJldHdlZW4gc2FtcGxlcwpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkeWVhci5kaXN0YW5jZSA8LSBhYnMoYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkeWVhci50MSkgLSBhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSR5ZWFyLnQyKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRTYW1wbGVfWWVhci5kaXN0YW5jZSA8LSBhYnMoYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkU2FtcGxlX1llYXIudDEpIC0gYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkU2FtcGxlX1llYXIudDIpKQoKIyBZZWFycyBiZXR3ZWVuIGRlY2ltYWwgZGF0ZSAobW9yZSBwcmVjaXNlIHRlbXBvcmFsIGRpc3RhbmNlKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlIDwtIGFicyhhcy5udW1lcmljKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkYXRlLmRlY2ltYWwudDEpIC0gYXMubnVtZXJpYyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGF0ZS5kZWNpbWFsLnQyKSkKCiMgRXBpZGVtaW9sb2dpY2FsIHRpbWUgYmV0d2VlbiAtIGNhdGFnb3JpY2FsClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRlcGkudGltZS5kaXN0YW5jZS5jYXQgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8MS8xMiwibW9udGgiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9My8xMiwgInF1YXJ0ZXIiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9Ni8xMiwgImhhbGYgeWVhciIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD0xLCAiMSB5ZWFyIixpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MiwgIjIgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MywgIjMgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9NCwgIjQgeWVhcnMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9NSwgIjUgeWVhcnMiLCAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTYsICI2IHllYXJzIiwiPjYgeWVhcnMiKSkpKSkpKSkpCgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZXBpLnRpbWUuZGlzdGFuY2UuY2F0IDwtIGZhY3RvcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZXBpLnRpbWUuZGlzdGFuY2UuY2F0LCBsZXZlbHM9YygibW9udGgiLCAicXVhcnRlciIsImhhbGYgeWVhciIsIjEgeWVhciIsICIyIHllYXJzIiwgIjMgeWVhcnMiLCAiNCB5ZWFycyIsICI1IHllYXJzIiwgIjYgeWVhcnMiLCAiPjYgeWVhcnMiKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRlcGkudGltZS5kaXN0YW5jZS5jYXQueWVhcnMgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTEsICIwIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTIsICIxIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTMsICIyIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTQsICIzIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRkZWNpbWFsLmRhdGUuZGlzdGFuY2U8PTUsICI0IiwgIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD02LCAiNSIsIj41IikpKSkpKQoKCiMgU2FtZSBjb3VudHJ5ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLmNvdW50cnkgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRHZW9fQ291bnRyeS50MSA9PSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkR2VvX0NvdW50cnkudDIsICJzYW1lIiwgImRpZmZlcmVudCIpCgojIElzIFVLClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRib3RoLnVrIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkaXMuVUsudDEgPT0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGlzLlVLLnQyLCAic2FtZSIsICJkaWZmZXJlbnQiKQoKIyBJcyBQSEUKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGJvdGguUEhFIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkaXMuUEhFLnQxID09IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRpcy5QSEUudDIsICJzYW1lIiwgImRpZmZlcmVudCIpCgojIFNhbWUgVFBBIExpbmVhZ2UgKGNsZWFuZWQgdXAgY2xhc3NpZmljYXRpb25zKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZSA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQV9MaW5lYWdlLnQxPT1QSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVFBBX0xpbmVhZ2UudDIsICJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5MaW5lYWdlIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24oeCkgaWZlbHNlKChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkVFBBX0xpbmVhZ2UudDFbeF09PSIwIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEFfTGluZWFnZS50Mlt4XT09IjAiKSxOQSxQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZVt4XSkpCgojIFNhbWUgVFBBIHN1YmxpbmVhZ2UKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLlBpbmVjb25lLmNsdXN0ZXIgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLnQyLCJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5QaW5lY29uZS5jbHVzdGVyIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24oeCkgaWZlbHNlKCgoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlW3hdPT0iZGlmZmVyZW50IiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50MVt4XT09IlNpbmdsZXRvbiIpIHwoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlW3hdPT0iZGlmZmVyZW50IiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRUUEEucGluZWNvbmUuc3VibGluZWFnZS50Mlt4XT09IlNpbmdsZXRvbiIpKSwiZGlmZmVyZW50IixQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuUGluZWNvbmUuY2x1c3Rlclt4XSkpCgojIERlZmluZSBHZW5ldGljIHJlbGF0aW9uc2hpcHMgaGllcmFyY2hpY2FsbHkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbm9taWMuY2x1c3Rlci5oaWVyYXJjaHkgPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSREaXN0YW5jZT09MCwiWmVyb19TTlBzIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5QaW5lY29uZS5jbHVzdGVyPT0ic2FtZSIsIlNhbWUgU3VibGluZWFnZSIsIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5UUEEuTGluZWFnZT09InNhbWUiLCAiU2FtZSBMaW5lYWdlIiwiRGlmZmVyZW50IExpbmVhZ2UiKSkpCgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2Vub21pYy5jbHVzdGVyLmhpZXJhcmNoeS5waCA8LSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJERpc3RhbmNlLlBoeWxvPT0wLCJaZXJvX1NOUHMiLCBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuVFBBLlBpbmVjb25lLmNsdXN0ZXI9PSJzYW1lIiwiU2FtZSBTdWJsaW5lYWdlIiwgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRzYW1lLlRQQS5MaW5lYWdlPT0ic2FtZSIsICJTYW1lIExpbmVhZ2UiLCJEaWZmZXJlbnQgTGluZWFnZSIpKSkKCgojIFNhbWUgUEhFIHJlZ2lvbgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkc2FtZS5QSEUucmVnaW9uIDwtIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkcGhlX2NlbnRyZS50MT09UEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHBoZV9jZW50cmUudDIsICJzYW1lIiwgImRpZmZlcmVudCIpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRQSEUuY2VudHJlLmNvbWJpbmF0aW9uIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24gKHgpIHBhc3RlMChzb3J0KGMoYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQxW3hdKSxhcy5jaGFyYWN0ZXIoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHBoZV9jZW50cmUudDJbeF0pKSksY29sbGFwc2U9Il9fXyIpKQoKIyBkb2VzIHRoZSBjb21iaW5hdGlvbiBpbmNsdWRlZCBMb25kb24/ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRpbnZvbHZlcy5Mb25kb24gPC0gaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQxPT0iTG9uZG9uIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRwaGVfY2VudHJlLnQyPT0iTG9uZG9uIiwgIkxvbmRvbiIsICJub3QtTG9uZG9uIikKCgojIE9yaWVudGF0aW9uIHBhaXIKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJE9yaWVudGF0aW9uX2NvbWJpbmF0aW9uIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhKSwgZnVuY3Rpb24gKHgpIHBhc3RlMChzb3J0KGMoYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0pLGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQyW3hdKSkpLGNvbGxhcHNlPSJfX18iKSkKCiNQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkT3JpZW50YXRpb24uQ2xhc3MgPC0gc2FwcGx5KDE6bnJvdyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEpLCBmdW5jdGlvbiAoeCkgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJNU00iICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09Ik1TTSIsICJNU00iLAojICAgICAgIGlmZWxzZShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQxW3hdPT0iTVNNIiB8IFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF09PSJNU00iLCAiTWl4ZWQiLCAKIyAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJNU1ciICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09IldTTSIsIkhldGVyb3NleHVhbCIsIAojICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJXU00iICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50Mlt4XT09Ik1TVyIsIkhldGVyb3NleHVhbCIsIlVua25vd24iKSkpKSkKClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRPcmllbnRhdGlvbi5DbGFzcyA8LSBzYXBwbHkoMTpucm93KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSksIGZ1bmN0aW9uICh4KSBpZmVsc2UoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGdlbmRlcl9vcmllbnRhdGlvbi50MVt4XT09IkdCTVNNIiAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF09PSJHQk1TTSIsICJHQk1TTSIsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0gJWluJSBjKCJNU1ciLCJXU00iKSAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDJbeF0gJWluJSBjKCJNU1ciLCJXU00iKSwiSGV0ZXJvc2V4dWFsIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJHQk1TTSIgJiBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkZ2VuZGVyX29yaWVudGF0aW9uLnQyW3hdICVpbiUgYygiTVNXIiwiV1NNIiksICJNaXhlZCIsIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgaWZlbHNlKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF0gJWluJSBjKCJNU1ciLCJXU00iKSAmIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRnZW5kZXJfb3JpZW50YXRpb24udDFbeF09PSJHQk1TTSIsICJNaXhlZCIsICJVbmtub3duIikpKSkpCiAgICAgICAgICAgICAgICAgICAgCgoKIyBDb3VudHJ5IENvbXBhcmlzb25zIGxhYmVsClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRDb3VudHJ5X2NvbWJpbmF0aW9ucyA8LSBwYXN0ZTAoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJEdlb19Db3VudHJ5LnQxLCJfX18iLFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRHZW9fQ291bnRyeS50MikKCiMgU3Vic2V0IHRvIFBIRSBkYXRhIG9ubHkgKGVmZmVjdGl2ZWx5IGFscmVhZHkgZG9uZSwgYnV0IGxldCdzIGJlIGV4cGxpY2l0KQpQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVsoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJGJvdGgudWs9PSJzYW1lIiAmICBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkYm90aC5QSEU9PSJzYW1lIiksXQpQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhIDwtIFBIRS5UUEEuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRQSEUub25seT09IlBIRSIsXQoKUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSRib3RoLnVrPT0ic2FtZSIpLF0KCmBgYApcClwKCmBgYHtyfQojIE1ha2Ugc2luZ2xlIHNpZGVkClBIRS5UUEEuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEgPC0gUEhFLlRQQS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YVshZHVwbGljYXRlZChQSEUuVFBBLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJFRheGFfY29tYmluYXRpb24pLF0KCmBgYAoKClwKXAojIyMgUGVyZm9ybSBhIG1vcmUgZGV0YWlsZWQgYW5hbHlzaXMgb2Ygc2FtcGxlcyBmcm9tIHRoZSBOb3J0aCBFYXN0IG9mIEVuZ2xhbmQKXApEbyBhIG1vcmUgZGV0YWlsZWQgZXhwbG9yYXRpb24gb2YgdGhlIE5vcnRoIEVhc3Qgb2YgRW5nbGFuZApcCmBgYHtyLCBmaWcuaGVpZ2h0PTMsIGZpZy53aWR0aD00fQpQSEUubWV0YWRhdGEubGlua2VkMi5yZWdpb25fTm9ydGhFYXN0IDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbUEhFLm1ldGFkYXRhLmxpbmtlZCRwaGVfY2VudHJlPT0iTm9ydGggRWFzdCIsXQoKIyBDb25zdHJhaW4gYnkgc2FtcGxlcyBiZWluZyBmcm9tIHRoZSBOb3J0aCBFYXN0ClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhWyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEkcGhlX2NlbnRyZS50MT09Ik5vcnRoIEVhc3QiICYgUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJHNhbWUuc2FtcGxlPT0iZGlmZmVyZW50IiksXQoKIyBDb25zdHJhaW4gYnkgdGhlIHNhbWUgUEhFIHJlZ2lvbgpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnNbUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyRzYW1lLlBIRS5yZWdpb249PSJzYW1lIixdCgojSnVzdCBwbG90IHRoZXNlIGRpc3Ryb3MKcC5Ob3J0aEVhc3QuUGFpcndpc2UuU05Qcy51bmNvbnN0cmFpbmVkIDwtIGdncGxvdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLCBhZXMoRGlzdGFuY2UuUGh5bG8pKSArIAogIGdlb21faGlzdG9ncmFtKGJpbndpZHRoID0gMSkgKwogIHRoZW1lX2J3KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArCiAgbGFicyh4PSJQYWlyd2lzZSBTTlAgRGlzdGFuY2UiLCB5PSJDb21wYXJpc29uIENvdW50IikKCnAuTm9ydGhFYXN0LlBhaXJ3aXNlLlNOUHMudW5jb25zdHJhaW5lZApgYGAKClwKTWFrZSBhIHNpbmdsZSBsaW5rYWdlIG5ldHdvcmsgZnJvbSB0aGUgTm9ydGggRWFzdCBzYW1wbGVzCmBgYHtyfQoKIyBDb25zdHJhaW4gYnkgU05QIGRpc3RhbmNlIChsb29zZXIgdGhhbiBwcmV2aW91c2x5IC0gd2UganVzdCB3YW50IHRvIGZpbmQgYmFzaWMgZ3JvdXBpbmdzIHdpdGhpbiBzdWJsaW5lYWdlIDEgZm9yIE5FIHNhbXBsZXMpClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVyc1tQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzJERpc3RhbmNlLlBoeWxvPD0yLF0KCiMgQW5kIG1ha2Ugc3VyZSB0aGF0IHdlIGFjdHVhbGx5IGhhdmUgZ2VuZXRpYyBkaXN0YW5jZSBkYXRhIGZvciBhbGwgc2FtcGxlcyB3aXRoaW4gdGhlIG5ldHdvcmsKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzWyFpcy5uYShQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzJERpc3RhbmNlLlBoeWxvKSxdCgojIGNsZWFudXAgc29tZSBkYXRhIG5vaXNlClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVyc1shaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycyR5ZWFyLnQxKSxdCgojIHByZXBhcmUgaW50cHV0IGRhdGEgKHdpdGggZWRnZSBpbmZvKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzWyxjKCJUYXhhMSIsIlRheGEyIiwiRGlzdGFuY2UuUGh5bG8iLCJkZWNpbWFsLmRhdGUuZGlzdGFuY2UiLCJ5ZWFyLmRpc3RhbmNlIiwiT3JpZW50YXRpb24uQ2xhc3MiLCJlcGkudGltZS5kaXN0YW5jZS5jYXQiKV0KCiMjIyMjIyMjIyMjIwojIHNvbWUgaXNzdWVzIHdpdGggdXBkYXRlIHRvIFI0IC0gZG91YmxlIHNpZGVkIG1hdHJpeApQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRlZGdlbmFtZSA8LSBzYXBwbHkoMTpucm93KFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHNvcnQoYXMuY2hhcmFjdGVyKHVubGlzdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MVt4LGMoIlRheGExIiwiVGF4YTIiKV0pKSksY29sbGFwc2U9Il9fXyIpKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MVshZHVwbGljYXRlZChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRlZGdlbmFtZSksXQoKIyBBbHNvIGhhdmluZyBhbiBpc3N1ZSB3aXRoIHRheGEgYXMgZmFjdG9ycyBoZXJlClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJFRheGExIDwtIGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSRUYXhhMSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLk5vcnRoRWFzdC5jbHVzdGVycy5pbnB1dDEkVGF4YTIgPC0gYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxJFRheGEyKQojIyMjIyMjIyMjIyMKCiNpbnZlcnNlIHdlaWdodApQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSREaXN0YW5jZS5pbnYgPC0gMS9QSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuTm9ydGhFYXN0LmNsdXN0ZXJzLmlucHV0MSREaXN0YW5jZS5QaHlsbwoKIyBNYWtlIGFjdHVhbCBuZXR3b3JrCnNldC5zZWVkKDEyMzUpClBIRS5Ob3J0aEVhc3QubmV0d29yayA8LSBuZXR3b3JrKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5Ob3J0aEVhc3QuY2x1c3RlcnMuaW5wdXQxLCBtYXRyaXgudHlwZSA9ICJlZGdlbGlzdCIsIGlnbm9yZS5ldmFsID0gRkFMU0UsIGRpcmVjdGVkID0gRikKClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBnZ25ldHdvcmsoUEhFLk5vcnRoRWFzdC5uZXR3b3JrLCBsYXlvdXQgPSAia2FtYWRha2F3YWkiLCB3ZWlnaHRzID0gIkRpc3RhbmNlLmludiIpClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyRUYXhhMSA8LSBQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuZ2ckdmVydGV4Lm5hbWVzCgojIGV4dHJhY3QgdGVtcG9yYWwgY2x1c3RlcnMgZnJvbSBuZXR3b3JrClBIRS5Ob3J0aEVhc3QubmV0d29yay5pZyA8LSBhc0lncmFwaChQSEUuTm9ydGhFYXN0Lm5ldHdvcmspClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzIDwtIGRhdGEuZnJhbWUoVGF4YTE9bmV0d29yay52ZXJ0ZXgubmFtZXMoUEhFLk5vcnRoRWFzdC5uZXR3b3JrKSwgdmVydGV4Lm5vPWFzLnZlY3RvcihWKFBIRS5Ob3J0aEVhc3QubmV0d29yay5pZykpLCBjbHVzdGVyPWlncmFwaDo6Y29tcG9uZW50cyhQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuaWcpJG1lbWJlcnNoaXApCiMgRm9yIGVhc2Ugb2Ygc3RvcnkgdGVsbGluZyBpbiB0aGUgcGFwZXIsIGZsaXAgY2x1c3RlcnMgMiBhbmQgMyBhcm91bmQgKHNvIHdlIGNhbiB0YWxrIGFib3V0IDIgZmlyc3QpClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzIDwtIFBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzICU+JQogIGRwbHlyOjptdXRhdGUoY2x1c3Rlci5vbGQ9Y2x1c3RlciwgY2x1c3Rlcj1pZmVsc2UoY2x1c3Rlci5vbGQ9PTIsIDMsIGlmZWxzZShjbHVzdGVyLm9sZD09MywyLGNsdXN0ZXIub2xkKSkpClBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIgPC0gcGFzdGUwKCJDbHVzdGVyIixQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRjbHVzdGVyKQoKIyBtZXJnZSBtZXRhZGF0YSBiYWNrIGluClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBwbHlyOjpqb2luKFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSxieT0iVGF4YTEiLCB0eXBlPSJsZWZ0IikKClBIRS5Ob3J0aEVhc3QubmV0d29yay5nZyA8LSBwbHlyOjpqb2luKFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRUYXhhMSwgQ2x1c3Rlcj1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSwgYnk9IlRheGExIiwgdHlwZT0ibGVmdCIpCgpgYGAKXApQbG90IG5ldHdvcmsKYGBge3J9CiMgUGxvdCBuZXR3b3JrCnAuUEhFLk5vcnRoRWFzdC5uZXR3b3JrLjJTTlAgPC0gZ2dwbG90KFBIRS5Ob3J0aEVhc3QubmV0d29yay5nZywgYWVzKHggPSB4LCB5ID0geSwgeGVuZCA9IHhlbmQsIHllbmQgPSB5ZW5kKSkgKyAKICBnZW9tX2VkZ2VzKGFscGhhPTAuOTAsIGN1cnZhdHVyZSA9IDAuMiwgYWVzKGNvbG9yPWZhY3RvcihEaXN0YW5jZS5QaHlsbyksIGxpbmV0eXBlPWZhY3RvcihEaXN0YW5jZS5QaHlsbykpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1jKCJncmV5NSIsImdyZXk1NSIsImdyZXk4NSIpLCBuYW1lPSJTTlBcbkRpc3RhbmNlIikgKwogIHNjYWxlX2xpbmV0eXBlKG5hbWU9IlNOUFxuRGlzdGFuY2UiKSArCiAgdGhlbWVfYmxhbmsoKSArCiAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2NvbG9yKCkgKyBnZ25ld3NjYWxlOjpuZXdfc2NhbGUoInNpemUiKSArCiAgZ2VvbV9ub2RlbGFiZWwoYWVzKGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbiwgbGFiZWw9cGFzdGUoVGF4YTEseWVhcixzZXA9IlxuIiksZm9udGZhY2UgPSAiYm9sZCIpLCBhbHBoYT0wLjgsIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbi0wLjQsIGxhYmVsLnNpemU9MC4xNSwgbGFiZWwucGFkZGluZyA9IHVuaXQoMC4wNSwgImxpbmVzIikpICsKICBnZW9tX25vZGVzKHNpemU9MS4wLCBhZXMoY29sb3I9Z2VuZGVyX29yaWVudGF0aW9uKSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iR2VuZGVyXG5PcmllbnRhdGlvbiIsIHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsgCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBOVUxMCnAuUEhFLk5vcnRoRWFzdC5uZXR3b3JrLjJTTlAKCmBgYAoKClwKT2ssIHNvIHRocmVlIG5ldHdvcmtzLiBDbGVhciBkaWZmZXJlbnRpYXRpb24gb2YgYSBoZXRlcm9zZXh1YWwgbmV0d29yayAod2l0aCAwLXNucCBkaXN0YW5jZXMpIGFuZCB0d28gcHJlZG9taW5hbnRseSBNU00gbmV0d29ya3MKXApMZXQncyBsb29rIGF0IHRoZSBwaHlsb2dlbmV0aWMgY29udGV4dCBvZiB0aG9zZSBOb3J0aCBFYXN0IGNsdXN0ZXJzIHdlJ3ZlIGRlZmluZWQuClB1bGwgb3V0IHN1YnRyZWVzIChmcm9tIHN1YmxpbmVhZ2UgMSBzdWJ0cmVlKQpcCmBgYHtyLCBmaWcuaGVpZ2h0PTEyLCBmaWcud2lkdGg9MTJ9CiMgQ2x1c3RlciAxCkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEgPC0gZ2V0TVJDQShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLCBQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50c1tQSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyPT0iQ2x1c3RlcjEiLCJUYXhhMSJdKQpCZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUgPC0gdHJlZV9zdWJzZXQoZnVsbC5iZWFzdDIudHJlZSwgbm9kZT1CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLCBsZXZlbHNfYmFjaz0wKQoKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUgPC0gcGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfUEhFX21ldGFkYXRhKHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX0hQRChCZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUsIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCIyMDE5LTA2LTAxIiksIFRQQS5tZXRhMi4xLCBQSEUubWV0YWRhdGEubGlua2VkLCBpbml0aWFsLnRyYWNrLm9mZnNldCA9IDEwKQoKIyBDYW4ndCBmaXQgaW4gdGlwIGxhYnMsIGJ1dCBzaW5jZSB0aGlzIGlzIGEgcG9seXBoeWxldGljIHN1YnRyZWUsIGl0IHdvdWxkIGJlIGhlbHBmdWwgdG8gYWRkIGEgdHJhY2sgdG8gaGlnaGxpZ2h0IHRoZSBORSBzdHJhaW5zClBIRS5tZXRhZGF0YS5saW5rZWQkaXMuTm9ydGhFYXN0IDwtIGlmZWxzZShQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmU9PSJOb3J0aCBFYXN0IiwiTm9ydGggRWFzdCIsICJPdGhlciBFbmdsYW5kIikKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5tZXRhZGF0YS5saW5rZWQkU2FtcGxlX05hbWUsIGBOb3J0aCBFYXN0YD1QSEUubWV0YWRhdGEubGlua2VkJGlzLk5vcnRoRWFzdCksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTEwKyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkVuZ2xhbmQiLCB2YWx1ZXM9YygiI0E2Q0VFMyIsImdyZXk5NSIpLCBicmVha3M9YygiTm9ydGggRWFzdCIsIk90aGVyIEVuZ2xhbmQiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCgojIEp1c3QgY29uZmlybSB0aGUgQ2x1c3RlcklEcyBmb3IgdGhpcyBzdWJ0cmVlIChtYWtlIHN1cmUgaXQgZG9lc24ndCBlbmNsb3NlIG90aGVyIGNsdXN0ZXJzKQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCA8LSBnaGVhdG1hcChwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCwgZGF0YS5mcmFtZShyb3cubmFtZXM9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXJJRD1QSEUuTm9ydGhFYXN0Lm5ldHdvcmsuY29tcG9uZW50cyRDbHVzdGVyKSwgY29sb3I9TlVMTCx3aWR0aD0oMS9tYXgocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUkZGF0YSRoZWlnaHQpKjMpLCBvZmZzZXQ9MTArKDQqNiksY29sbmFtZXNfYW5nbGU9LTQ1LGNvbG5hbWVzX29mZnNldF95PTAuMDIsIGhqdXN0PS0wLjAsIGZvbnQuc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluKSArIAogICAgc2NhbGVfZmlsbF9tYW51YWwobmFtZT0iTm9ydGggRWFzdFxuQ2x1c3RlciIsIHZhbHVlcz1jKCIjN2ZjOTdmIiwiI2JlYWVkNCIsIiNmZGMwODYiKSwgYnJlYWtzPWMoIkNsdXN0ZXIxIiwiQ2x1c3RlcjIiLCJDbHVzdGVyMyIpLCBuYS52YWx1ZSA9ICJ3aGl0ZSIsIGd1aWRlID0gZ3VpZGVfbGVnZW5kKG9yZGVyID0gNikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKCiMgYWRkIGEgYml0IG1vcmUgcm9vbSB0byB0aGUgeCBheGlzCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LnguYXhpcy5saW1pdHMgPC0gZ2dwbG90X2J1aWxkKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklEKSRsYXlvdXQkcGFuZWxfc2NhbGVzX3hbWzFdXSRyYW5nZSRyYW5nZQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCA8LSBwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRCArIAogICAgY29vcmRfY2FydGVzaWFuKHg9YyhwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzWzFdLHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LnguYXhpcy5saW1pdHNbMl0rNCksIHk9YygtMC41LShsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0JGRhdGEkbGFiZWwpKS8xNSksbGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCRkYXRhJGxhYmVsKSkrMikpICsKICB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjUsMCwwLDAsIHVuaXQ9Im1tIikpCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjEuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC53aXRoX2NsdXN0ZXJJRAoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMKIyBDbHVzdGVyIDIKQmVhc3QudHJlZS5ORS5jbHVzdGVyMiA8LSBnZXRNUkNBKGZ1bGwuYmVhc3QyLnRyZWVAcGh5bG8sIFBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzW1BIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXI9PSJDbHVzdGVyMiIsIlRheGExIl0pCkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSA8LSB0cmVlX3N1YnNldChmdWxsLmJlYXN0Mi50cmVlLCBub2RlPUJlYXN0LnRyZWUuTkUuY2x1c3RlcjIsIGxldmVsc19iYWNrPTEpCgpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSA8LSBwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9QSEVfbWV0YWRhdGEocGxvdF9iZWFzdF9zdWJ0cmVlX3dpdGhfSFBEKEJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIjIwMTktMDYtMDEiKSwgVFBBLm1ldGEyLjEsIFBIRS5tZXRhZGF0YS5saW5rZWQsIGluaXRpYWwudHJhY2sub2Zmc2V0ID0gMjApICsgZ2VvbV90aXBsYWIoc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBhbGlnbj1ULCBvZmZzZXQ9NSwgbGluZXNpemU9MC40KQojIEp1c3QgYWRkIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSB0byBoaWdobGlnaHQKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTIwKyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkNsdXN0ZXIiLCB2YWx1ZXM9YygiIzdmYzk3ZiIsIiNiZWFlZDQiLCIjZmRjMDg2IiksIGJyZWFrcz1jKCJDbHVzdGVyMSIsIkNsdXN0ZXIyIiwiQ2x1c3RlcjMiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKIyBhZGQgYSBiaXQgbW9yZSByb29tIHRvIHRoZSB4IGF4aXMKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUueC5heGlzLmxpbWl0cyA8LSBnZ3Bsb3RfYnVpbGQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUpJGxheW91dCRwYW5lbF9zY2FsZXNfeFtbMV1dJHJhbmdlJHJhbmdlCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlIDwtIHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlICsgCiAgICBjb29yZF9jYXJ0ZXNpYW4oeD1jKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlLnguYXhpcy5saW1pdHNbMV0scC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUueC5heGlzLmxpbWl0c1syXSsxMiksIHk9YygtMC41LShsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkbGFiZWwpKS8yMCktMSxsZW5ndGgodW5pcXVlKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMi5zdWJ0cmVlJGRhdGEkbGFiZWwpKSswLjUpKSArIAogIHRoZW1lKGxlZ2VuZC5tYXJnaW4gPSBtYXJnaW4oLTAuNSwwLDAsMCwgdW5pdD0ibW0iKSkKCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZQoKIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIwojIENsdXN0ZXIgMwpCZWFzdC50cmVlLk5FLmNsdXN0ZXIzIDwtIGdldE1SQ0EoZnVsbC5iZWFzdDIudHJlZUBwaHlsbywgUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHNbUEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3Rlcj09IkNsdXN0ZXIzIiwiVGF4YTEiXSkKQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIDwtIHRyZWVfc3Vic2V0KGZ1bGwuYmVhc3QyLnRyZWUsIG5vZGU9QmVhc3QudHJlZS5ORS5jbHVzdGVyMywgbGV2ZWxzX2JhY2s9MSkKCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIDwtIHBsb3RfYmVhc3Rfc3VidHJlZV93aXRoX1BIRV9tZXRhZGF0YShwbG90X2JlYXN0X3N1YnRyZWVfd2l0aF9IUEQoQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwiMjAxOS0wNi0wMSIpLCBUUEEubWV0YTIuMSwgUEhFLm1ldGFkYXRhLmxpbmtlZCwgaW5pdGlhbC50cmFjay5vZmZzZXQgPSAyNikgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4sIGFsaWduPVQsIG9mZnNldD0zLCBsaW5lc2l6ZT0wLjQpCgojIEp1c3QgYWRkIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSB0byBoaWdobGlnaHQKcC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlJGRhdGEkaGVpZ2h0KSozKSwgb2Zmc2V0PTI2Kyg0KjUpLGNvbG5hbWVzX2FuZ2xlPS00NSxjb2xuYW1lc19vZmZzZXRfeT0wLjAyLCBoanVzdD0tMC4wLCBmb250LnNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKyAKICAgIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9Ik5vcnRoIEVhc3RcbkNsdXN0ZXIiLCB2YWx1ZXM9YygiIzdmYzk3ZiIsIiNiZWFlZDQiLCIjZmRjMDg2IiksIGJyZWFrcz1jKCJDbHVzdGVyMSIsIkNsdXN0ZXIyIiwiQ2x1c3RlcjMiKSwgbmEudmFsdWUgPSAid2hpdGUiLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDUsIG5jb2w9MikpICsKICAgIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9maWxsKCkKCiMgYWRkIGEgYml0IG1vcmUgcm9vbSB0byB0aGUgeCBheGlzCnAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLnguYXhpcy5saW1pdHMgPC0gZ2dwbG90X2J1aWxkKHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlKSRsYXlvdXQkcGFuZWxfc2NhbGVzX3hbWzFdXSRyYW5nZSRyYW5nZQpwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSA8LSBwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSArIAogICAgY29vcmRfY2FydGVzaWFuKHg9YyhwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZS54LmF4aXMubGltaXRzWzFdLHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLnguYXhpcy5saW1pdHNbMl0rMTIpLCB5PWMoLTAuNS0obGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSRkYXRhJGxhYmVsKSkvMjApLTEsbGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZSRkYXRhJGxhYmVsKSkrMC41KSkgKyAKICB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjUsMCwwLDAsIHVuaXQ9Im1tIikpCiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZQoKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0LndpdGhfY2x1c3RlcklECiNwLkJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZSAKI3AuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlIApgYGAKClwKU2luY2UgQ2x1c3RlciAxIGlzIHJlYWxseSBxdWl0ZSBwb2x5cGh5bGV0aWMsIGl0IG1heWJlIG1vcmUgdXNlZnVsIHRvIHNob3cgdGhlIGNsdXN0ZXJzIGluIGNvbnRleHQgZm9yIHRoYXQgb25lCgpgYGB7cn0KIyBBZGQgTm9ydGggRWFzdCBpZGVudGlmaWVyIGNvbHVtbgpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCA8LSBnaGVhdG1hcChzdWJsaW5lYWdlLjEudHJlZS5oZWF0bWFwLCBkYXRhLmZyYW1lKHJvdy5uYW1lcz1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBgTm9ydGggRWFzdGA9UEhFLm1ldGFkYXRhLmxpbmtlZCRpcy5Ob3J0aEVhc3QpLCBjb2xvcj1OVUxMLHdpZHRoPSgxL21heChzdWJsaW5lYWdlLjEudHJlZS5oZWF0bWFwJGRhdGEkaGVpZ2h0KSozKSoxLjIsIG9mZnNldD0wKyg0KjUpKjEuMixjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJOb3J0aCBFYXN0XG5FbmdsYW5kIiwgdmFsdWVzPWMoIiNBNkNFRTMiLCJncmV5OTUiKSwgYnJlYWtzPWMoIk5vcnRoIEVhc3QiLCJPdGhlciBFbmdsYW5kIiksIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA1KSkgKwogICAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKQoKIyBKdXN0IGNvbmZpcm0gdGhlIENsdXN0ZXJJRHMgZm9yIHRoaXMgc3VidHJlZSAobWFrZSBzdXJlIGl0IGRvZXNuJ3QgZW5jbG9zZSBvdGhlciBjbHVzdGVycykKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gZ2hlYXRtYXAocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQsIGRhdGEuZnJhbWUocm93Lm5hbWVzPVBIRS5Ob3J0aEVhc3QubmV0d29yay5jb21wb25lbnRzJFRheGExLCBDbHVzdGVySUQ9UEhFLk5vcnRoRWFzdC5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGNvbG9yPU5VTEwsd2lkdGg9KDEvbWF4KHAuQmVhc3QudHJlZS5zdWJsaW5lYWdlMS5ORS5zdWJ0cmVlLmNsdXN0ZXIuaGlnaGxpZ2h0JGRhdGEkaGVpZ2h0KSozKSoxLjIsIG9mZnNldD0wKyg0KjYpKjEuMixjb2xuYW1lc19hbmdsZT0tNDUsY29sbmFtZXNfb2Zmc2V0X3k9MC4wMiwgaGp1c3Q9LTAuMCwgZm9udC5zaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pICsgCiAgICBzY2FsZV9maWxsX21hbnVhbChuYW1lPSJOb3J0aCBFYXN0XG5DbHVzdGVyIiwgdmFsdWVzPWMoIiM3ZmM5N2YiLCIjYmVhZWQ0IiwiI2ZkYzA4NiIpLCBicmVha3M9YygiQ2x1c3RlcjEiLCJDbHVzdGVyMiIsIkNsdXN0ZXIzIiksIG5hLnZhbHVlID0gIndoaXRlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSA2LCBuY29sPTIpKSArCiAgICBnZ25ld3NjYWxlOjpuZXdfc2NhbGVfZmlsbCgpCgojIGFkZCBhIGJpdCBtb3JlIHJvb20gdG8gdGhlIHggYXhpcwpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzIDwtIGdncGxvdF9idWlsZChwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCkkbGF5b3V0JHBhbmVsX3NjYWxlc194W1sxXV0kcmFuZ2UkcmFuZ2UKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgKyAKICAgIGNvb3JkX2NhcnRlc2lhbih4PWMocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQueC5heGlzLmxpbWl0c1sxXSxwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodC54LmF4aXMubGltaXRzWzJdKzQpLCB5PWMoLTAuNS0obGVuZ3RoKHVuaXF1ZShwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodCRkYXRhJGxhYmVsKSkvMTUpLGxlbmd0aCh1bmlxdWUocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQkZGF0YSRsYWJlbCkpKzIpKQoKIyByZWR1Y2Ugc3BhY2luZyBiZXR3ZWVuIGxlZ2VuZCBzY2FsZXMKcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgPC0gcC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQgKyB0aGVtZShsZWdlbmQubWFyZ2luID0gbWFyZ2luKC0wLjk1LDAsMCwwLCB1bml0PSJtbSIpKQpwLkJlYXN0LnRyZWUuc3VibGluZWFnZTEuTkUuc3VidHJlZS5jbHVzdGVyLmhpZ2hsaWdodAoKYGBgCgoKXCAKUGxvdCB0b2dldGhlcgpgYGB7ciwgZmlnLmhlaWdodD0xMCwgZmlnLndpZHRoPTEwfQoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMSA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIyLnN1YnRyZWUsIHAuQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlLCBuY29sPTEsIGxhYmVscz1jKCJDIC0gQ2x1c3RlciAyIiwgIkQgLSBDbHVzdGVyIDMiKSwgdmp1c3Q9MS4wLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZT0wLjk1KQoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMiA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLk5FLmNsdXN0ZXIxLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQud2l0aF9jbHVzdGVySUQsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTEsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDMsMiksIGxhYmVscz1jKCJCIC0gQ2x1c3RlciAxIiwgIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQpwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkyCgoKcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMyA8LSBwbG90X2dyaWQocC5CZWFzdC50cmVlLnN1YmxpbmVhZ2UxLk5FLnN1YnRyZWUuY2x1c3Rlci5oaWdobGlnaHQsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTEsIG5jb2w9MiwgcmVsX3dpZHRocz1jKDgsNyksIGxhYmVscz1jKCJCIC0gU3VibGluZWFnZSAxIChBbGwpIiwgIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplLCBzY2FsZT0wLjk1LCB2anVzdD0xLjApCgpwLkJlYXN0LnRyZWUuTkUuc3VidHJlZXMuY29tYmkzCgpgYGAKClwKXApMb29rIG1vcmUgY2xvc2VseSBhdCBwb3B1bGF0aW9uIGRlbW9ncmFwaGljcyBvZiB0aGVzZSBjbHVzdGVycwpgYGB7cn0KIyBNZXRhZGF0YSBvbiBORSBjbHVzdGVyIDIKUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjIuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwpICU+JQogIGRwbHlyOjpncm91cF9ieShHZW9fQ291bnRyeSwgaXMuTm9ydGhFYXN0LCBnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKQoKIyBNZXRhZGF0YSBvbiBORSBjbHVzdGVyIDMKUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwpICU+JQogIGRwbHlyOjpncm91cF9ieShHZW9fQ291bnRyeSwgaXMuTm9ydGhFYXN0LCBnZW5kZXJfb3JpZW50YXRpb24pICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQ9bigpKQoKIyBDb3VudHJ5IGluZm8gb24gTkUgY2x1c3RlciAzClRQQS5tZXRhMi4xICU+JSAKICBkcGx5cjo6ZmlsdGVyKFNhbXBsZV9OYW1lICVpbiUgQmVhc3QudHJlZS5ORS5jbHVzdGVyMy5zdWJ0cmVlQHBoeWxvJHRpcC5sYWJlbCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KEdlb19Db3VudHJ5KSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkKCiMgU2VwYXJhdGUgbWV0YWRhdGEgcmVjb3JkcyBzaG93IEh1bmdhcmlhbiBzYW1wbGUgIlRQQV9IVU4xODAwMDEiIGNhbWUgZnJvbSBhIG1hbGUgYmlzZXh1YWwgKE1TV00pLgpgYGAKXApFeGFtaW5lIFNOUCBzY2FsZWQgdHJlZSBmb3IgZGlzdGFuY2VzCmBgYHtyfQoKIyBFeHRyYWN0IGluZm9ybWF0aW9uIGFib3V0IFNOUCBkaXN0YW5jZXMKVFBBLk5FY2x1c3RlcjMucHlqYXJ0cmVlLm1yY2EgPC0gZ2V0TVJDQShUUEEucHlqYXIudHJlZSwgYXMuY2hhcmFjdGVyKHVubGlzdChUUEEubWV0YTIuMVtUUEEubWV0YTIuMSRTYW1wbGVfTmFtZSAlaW4lIEJlYXN0LnRyZWUuTkUuY2x1c3RlcjMuc3VidHJlZUBwaHlsbyR0aXAubGFiZWwsIlNhbXBsZV9OYW1lIl0pKSkKCgpUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSA8LSB0cmVlX3N1YnNldChUUEEucHlqYXIudHJlZSwgbm9kZT1UUEEuTkVjbHVzdGVyMy5weWphcnRyZWUubXJjYSwgbGV2ZWxzX2JhY2s9MSkKCmdndHJlZShUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSkgKyBnZW9tX3RpcGxhYihzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4pCmdndHJlZShUUEEuTkVjbHVzdGVyMy5weWphcnRyZWUuc3VidHJlZSkkZGF0YQpgYGAKClwKXApEbyBzb21lIGFuYWx5c2lzIG9mIG5lYXJlc3QgbmVpZ2hib3VyIGFuZCBkaXN0YW5jZXMgdG8gTVJDQXMKYGBge3J9CmNhbGN1bGF0ZS55ZWFycy5mcm9tLm1yY2EgPC0gZnVuY3Rpb24oY3VycmVudC5nZ3RyZWUucGh5bG8sIGN1cnJlbnQuZ2d0cmVlLmRhdGEpewogICNjdXJyZW50LmdndHJlZSA8LSBCZWFzdC50cmVlLk5FLmNsdXN0ZXIzLnN1YnRyZWUKICBhbGwudGlwcyA8LSBjdXJyZW50LmdndHJlZS5waHlsbyR0aXAubGFiZWwKICBkaXN0LjIubXJjYSA8LSBOVUxMCiAgIyMjIHB1dCBkYXRlcyBpbnRvIGRmCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLm1lZGlhbiA8LSAyMDE5LjUgLSBjdXJyZW50LmdndHJlZS5kYXRhJGhlaWdodF9tZWRpYW4KICBjdXJyZW50LmdndHJlZS5kYXRhJHllYXIgPC0gYXMubnVtZXJpYyhyb3VuZCgyMDE5LjUgLSBjdXJyZW50LmdndHJlZS5kYXRhJGhlaWdodF9tZWRpYW4sMykpCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLjk1aGlnaCA8LSByb3VuZCgyMDE5LjUgLSBzYXBwbHkoMTpucm93KGN1cnJlbnQuZ2d0cmVlLmRhdGEpLGZ1bmN0aW9uKHgpIGFzLm51bWVyaWModW5saXN0KGN1cnJlbnQuZ2d0cmVlLmRhdGFbeCwiaGVpZ2h0XzAuOTVfSFBEIl0pKVsxXSksIDMpCiAgY3VycmVudC5nZ3RyZWUuZGF0YSRtcmNhLjk1bG93IDwtIHJvdW5kKDIwMTkuNSAtIHNhcHBseSgxOm5yb3coY3VycmVudC5nZ3RyZWUuZGF0YSksZnVuY3Rpb24oeCkgYXMubnVtZXJpYyh1bmxpc3QoY3VycmVudC5nZ3RyZWUuZGF0YVt4LCJoZWlnaHRfMC45NV9IUEQiXSkpWzJdKSwgMykKICAjIGV4dHJhY3QgZGF0ZXMgYmV0d2VlbiBzYW1wbGUgYW5kIGl0cyBNUkNBIHVzaW5nIGxvb3AKICBmb3IgKGN1cnJlbnQubm9kZSBpbiBhbGwudGlwcykgewogICAgY3VycmVudC5wYXJlbnQgPC0gYyhtYXRjaChjdXJyZW50Lm5vZGUsY3VycmVudC5nZ3RyZWUucGh5bG8kdGlwLmxhYmVsKSwgcGhhbmdvcm46OkFuY2VzdG9ycyhjdXJyZW50LmdndHJlZS5waHlsbywgbWF0Y2goYyhjdXJyZW50Lm5vZGUpLCBjdXJyZW50LmdndHJlZS5waHlsbyR0aXAubGFiZWwpLCAicGFyZW50IikpCiAgICAKICAgIGN1cnJlbnQubm9kZWxpc3QgPC0gY3VycmVudC5nZ3RyZWUuZGF0YVtjdXJyZW50LmdndHJlZS5kYXRhJG5vZGUgJWluJSBjdXJyZW50LnBhcmVudCxdCiAgICBjdXJyZW50LmRpc3QuMi5tcmNhIDwtIGMoY3VycmVudC5ub2RlLCBhcy5udW1lcmljKGN1cnJlbnQubm9kZWxpc3RbMSwieWVhciJdLWN1cnJlbnQubm9kZWxpc3RbMiwieWVhciJdKSkKICAgIGRpc3QuMi5tcmNhIDwtIHJiaW5kKGRpc3QuMi5tcmNhLCBjdXJyZW50LmRpc3QuMi5tcmNhKQogIH0KICBkaXN0LjIubXJjYSA8LSBkYXRhLmZyYW1lKFNhbXBsZV9OYW1lPWFzLmNoYXJhY3RlcihkaXN0LjIubXJjYVssMV0pLCBkaXN0LnRvLm1yY2E9YXMubnVtZXJpYyhkaXN0LjIubXJjYVssMl0pLCBzdHJpbmdzQXNGYWN0b3JzPUYpCiAgcmV0dXJuKGRpc3QuMi5tcmNhKQp9CgojIyMgQWxsIHNhbXBsZXMgaW4gZ2xvYmFsIHRyZWUKZGlzdC5tcmNhLmFsbC5UUEEgPC0gY2FsY3VsYXRlLnllYXJzLmZyb20ubXJjYShmdWxsLmJlYXN0Mi50cmVlQHBoeWxvLCBmdWxsLmJlYXN0Mi50cmVlQGRhdGEpCgpgYGAKXApNZXJnZSBkaXN0Mk1SQ0Egd2l0aCBtZXRhZGF0YQpgYGB7cn0KUEhFLm1ldGFkYXRhLmxpbmtlZC5kaXN0Mm1yY2EgPC0gbGVmdF9qb2luKFBIRS5tZXRhZGF0YS5saW5rZWQsIGRpc3QubXJjYS5hbGwuVFBBLCBieT0iU2FtcGxlX05hbWUiKQoKcC50aW1lMm1yY2Eub3JpZW50YXRpb24gPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoZ2VuZGVyX29yaWVudGF0aW9uLCBkaXN0LnRvLm1yY2EsIGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbikpICsgCiAgZ2VvbV9xdWFzaXJhbmRvbShzaXplPTAuNzUsIGFscGhhPTAuNSkgKwogIHRoZW1lX2xpZ2h0KCkgKyB0aGVtZS50ZXh0LnNpemUgKwogIGNvb3JkX2ZsaXAoKSArCiAgbGFicyh4PSJHZW5kZXIgT3JpZW50YXRpb24iLCB5PSJZZWFycyB0byBNUkNBIiwgY29sb3I9IkdlbmRlciBPcmllbnRhdGlvbiIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKG5hbWU9IkdlbmRlclxuT3JpZW50YXRpb24iLCB2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uKQoKcC50aW1lMm1yY2EucGhlX3JlZ2lvbiA8LSBnZ3Bsb3QoUEhFLm1ldGFkYXRhLmxpbmtlZC5kaXN0Mm1yY2EsIGFlcyhwaGVfY2VudHJlLCBkaXN0LnRvLm1yY2EsIGNvbG9yPXBoZV9jZW50cmUpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKHlsaW09YygwLDQwKSkgKwogIGxhYnMoeD0iVUtIU0EgUmVnaW9uIiwgeT0iWWVhcnMgdG8gTVJDQSIsIGNvbG9yPSJVS0hTQSBSZWdpb24iKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJVS0hTQVxuUmVnaW9uIiwgdmFsdWVzPVBIRS5yZWdpb24uY29scy5icmV3JHJlZ2lvbi5jb2wsIGJyZWFrcz1QSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pCgpwLnRpbWUybXJjYS5waGVfcmVnaW9uLm9yaWVudGF0aW9uIDwtIGdncGxvdChQSEUubWV0YWRhdGEubGlua2VkLmRpc3QybXJjYSwgYWVzKHBoZV9jZW50cmUsIGRpc3QudG8ubXJjYSwgY29sb3I9Z2VuZGVyX29yaWVudGF0aW9uKSkgKyAKICBnZW9tX3F1YXNpcmFuZG9tKHNpemU9MC43NSwgYWxwaGE9MC41KSArCiAgdGhlbWVfbGlnaHQoKSArIHRoZW1lLnRleHQuc2l6ZSArCiAgY29vcmRfZmxpcCh5bGltPWMoMCwyMCkpICsKICBsYWJzKHg9IlVLSFNBIFJlZ2lvbiIsIHk9IlllYXJzIHRvIE1SQ0EiKSArCiAgdGhlbWUobGVnZW5kLnBvc2l0aW9uPSdib3R0b20nLCBsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSkgKwogIHNjYWxlX2NvbG9yX21hbnVhbChuYW1lPSJHZW5kZXJcbk9yaWVudGF0aW9uIiwgdmFsdWVzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLmNvbHMsIGJyZWFrcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbikKcC50aW1lMm1yY2EucGhlX3JlZ2lvbi5vcmllbnRhdGlvbgoKCnAudGltZTJtcmNhLnN1YmxpbmVhZ2UgPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGRpc3QudG8ubXJjYSwgY29sb3I9VFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeD0iVFBBIExpbmVhZ2UiLCB5PSJZZWFycyB0byBNUkNBIiwgY29sb3I9IlRQQSBMaW5lYWdlIikgKwogIHRoZW1lKGxlZ2VuZC5wb3NpdGlvbj0nYm90dG9tJywgbGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIikpICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkKcC50aW1lMm1yY2Euc3VibGluZWFnZQoKCnAudGltZTJtcmNhLkxpbmVhZ2UgPC0gZ2dwbG90KFBIRS5tZXRhZGF0YS5saW5rZWQuZGlzdDJtcmNhLCBhZXMoVFBBX0xpbmVhZ2UsIGRpc3QudG8ubXJjYSwgY29sb3I9VFBBX0xpbmVhZ2UpKSArIAogIGdlb21fcXVhc2lyYW5kb20oc2l6ZT0wLjc1LCBhbHBoYT0wLjUpICsKICB0aGVtZV9saWdodCgpICsgdGhlbWUudGV4dC5zaXplICsKICBjb29yZF9mbGlwKCkgKwogIGxhYnMoeD0iVFBBIExpbmVhZ2UiLCB5PSJZZWFycyB0byBNUkNBIChNZWRpYW4gb2YgUG9zdGVyaW9yKSIsIGNvbG9yPSJUUEEgTGluZWFnZSIpICsKICB0aGVtZShsZWdlbmQucG9zaXRpb249J2JvdHRvbScsIGxlZ2VuZC5rZXkuc2l6ZSA9IHVuaXQoMC41NSwibGluZSIpKSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1UUEFfTGluZWFnZS5jb2xzJExpbmVhZ2UuY29sLCBicmVha3M9VFBBX0xpbmVhZ2UuY29scyRMaW5lYWdlKQpgYGAKXApcCk1heWJlIGNhbiBtYWtlIGFuIE1TVCBvZiB0aGUgTm9ydGggRWFzdCBzYW1wbGVzIGZvciBncmFwZXRyZWU/CmBgYHtyfQpUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0IDwtIGFwZTo6a2VlcC50aXAoVFBBLnB5amFyLnRyZWUsIGFzLmNoYXJhY3Rlcih1bmxpc3QoUEhFLm1ldGFkYXRhLmxpbmtlZFtQSEUubWV0YWRhdGEubGlua2VkJHBoZV9jZW50cmU9PSJOb3J0aCBFYXN0IiwiU2FtcGxlX05hbWUiXSkpKQoKI2dndHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0KQojd3JpdGUudHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQuTm9ydGhFYXN0LCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LU5vcnRoRWFzdC5weWphci4yMDIyLTAyLTI2LnRyZSIpKQoKIyBXcml0ZSBvdXQgYSBtZXRhZGF0YSBzaGVldCBmb3IgdGhlIHJlbGV2YW50IGluZm9ybWF0aW9uClBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlIDwtIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwgInllYXIiLCJnZW5kZXJfb3JpZW50YXRpb24iLCJwaGVfY2VudHJlIiwiaGl2cG9zIiwidWtib3JuIiwiVFBBX0xpbmVhZ2UiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIpXQpjb2xuYW1lcyhQSEUubWV0YWRhdGEubGlua2VkLmdyYXBldHJlZSlbMV0gPC0gIklEIgoKI3dyaXRlLnRhYmxlKFBIRS5tZXRhZGF0YS5saW5rZWQuZ3JhcGV0cmVlLCBwYXN0ZTAoRGF0YV9pbnB1dF9kaXJlY3RvcnksIlRQQS5VSy1vbmx5LmdyYXBldHJlZS5tZXRhLjIwMjItMDItMDMudHN2IiksIHNlcCA9ICJcdCIsIHF1b3RlPUYsIHJvdy5uYW1lcyA9IEYpCmBgYAoKCkFsdGVybmF0aXZlIGFwcHJvYWNoIHVzaW5nIE1TVCBpbnN0ZWFkIG9mIG5ldHdvcmtzIGZvciBOb3J0aCBFYXN0IGRhdGEKYGBge3J9CiMgUmVhZCBpbiBNU1QKI1RQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5maWxlIDwtIHBhc3RlMChEYXRhX2lucHV0X2RpcmVjdG9yeSwiVFBBLVVLLU5vcnRoRWFzdC0yMDIyLTAyLTI2LkdlbmRlck9yaWVudGF0aW9uLU1TVHJlZS5pbmtzY2FwZWQuK25vZGUtY291bnRzK0dCTVNNLnN2ZyIpCgpwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZSA8LSBnZ2RyYXcoKSArIGRyYXdfaW1hZ2UoVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLmZpbGUpCnAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlCgpwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5oZWFkZXIgPC0gcGxvdF9ncmlkKHAuVFBBLk5vcnRoRWFzdEVuZ2xhbmQuR3JhcGV0cmVlLCBsYWJlbHM9YygiQSAtIE5ldHdvcmsgQ2x1c3RlcnMgKE5vcnRoIEVhc3QgRW5nbGFuZCkiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGU9MC45NSkKCmBgYApcClBsb3Qgd2l0aCBiZWFzdCB0cmVlcwpgYGB7ciwgZmlnLmhlaWdodD0xMiwgZmlnLndpZHRoPTEyfQojcC5QSEUuTm9ydGhFYXN0X01TVC53aXRoLmJlYXN0LnN1YnRyZWVzLmNvbWJpIDwtIHBsb3RfZ3JpZChwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZSwgcC5CZWFzdC50cmVlLk5FLnN1YnRyZWVzLmNvbWJpMywgbmNvbD0xLCByZWxfaGVpZ2h0cz1jKDMsNiksIGxhYmVscz1jKCJBIC0gTmV0d29yayBDbHVzdGVycyAoTm9ydGggRWFzdCBFbmdsYW5kKSIsICIiKSwgbGFiZWxfc2l6ZT1wYW5lbC5sYWIuc2l6ZSwgc2NhbGUgPSAwLjk1KQoKcC5QSEUuTm9ydGhFYXN0X01TVC53aXRoLmJlYXN0LnN1YnRyZWVzLmNvbWJpIDwtIHBsb3RfZ3JpZChwLlRQQS5Ob3J0aEVhc3RFbmdsYW5kLkdyYXBldHJlZS5oZWFkZXIsIHAuQmVhc3QudHJlZS5ORS5zdWJ0cmVlcy5jb21iaTMsIG5jb2w9MSwgcmVsX2hlaWdodHM9YygzLDcpKQoKCgpwLlBIRS5Ob3J0aEVhc3RfTVNULndpdGguYmVhc3Quc3VidHJlZXMuY29tYmkKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIkZpZzNfU3VibGluMS5Ob3J0aEVhc3QuTVNUK0JlYXN0LiIsZm9ybWF0KFN5cy5EYXRlKCksIiVZJW0lZCIpLCIucGRmIiksIHVuaXRzPSdtbScsIHdpZHRoPTIwMCwgaGVpZ2h0PTI0NSwgZGV2aWNlPSdwZGYnLCBkcGk9MTIwMCkKCgpgYGAKClwKRG8gc29tZSBhbmFseXNpcyBvZiBtYWpvciBzdWJsaW5lYWdlcyBvdmVyIHRpbWUgYnkgcmVnaW9uIC0gY291bGQgdGhpcyBpbmZsdWVuY2Ugb2JzZXJ2YXRpb25zIGFib3V0IHN1YmxpbmVhZ2VzPwpgYGB7ciwgZmlnLmhlaWdodD02LCBmaWcud2lkdGg9NH0KIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGJ5IFBIRSBSZWdpb24KUEhFLm1ham9yLnN1YmxpbmVhZ2UuUEhFY2VudHJlLmRhdGUgPC0gUEhFLm1ldGFkYXRhLmxpbmtlZCAlPiUgCiAgZHBseXI6OmZpbHRlcihUUEEucGluZWNvbmUuc3VibGluZWFnZSAlaW4lIGMoMSwxNCkpICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgcGhlX2NlbnRyZSwgeWVhcikgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShDb3VudD1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUodG90YWwuc3VibGluPXN1bShDb3VudCkpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2MocGhlX2NlbnRyZSksIC5ieV9ncm91cD1UKSAlPiUKICBkcGx5cjo6bXV0YXRlKGZyYWN0aW9uPUNvdW50L3RvdGFsLnN1YmxpbiwgY3VtX2ZyYWN0PWN1bXN1bShmcmFjdGlvbiksIGN1bV9mcmFjdC5taWQgPSBjdW1fZnJhY3QtKGZyYWN0aW9uLzIpKQoKCmdncGxvdChQSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZSwgYWVzKHllYXIsIHBoZV9jZW50cmUsIHNpemU9Q291bnQsIGNvbG9yPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSkgKwogIGdlb21fcG9pbnQoKSArIAogIGZhY2V0X2dyaWQoLn5UUEEucGluZWNvbmUuc3VibGluZWFnZSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHRoZW1lLnRleHQuc2l6ZSArCiAgc2NhbGVfY29sb3JfbWFudWFsKHZhbHVlcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZS5jb2xzLCBicmVha3M9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UpCgoKcC5QSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZS5idWJibGVwbG90IDwtIGdncGxvdChQSEUubWFqb3Iuc3VibGluZWFnZS5QSEVjZW50cmUuZGF0ZSwgYWVzKHllYXIsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBjb2xvcj1UUEEucGluZWNvbmUuc3VibGluZWFnZSkpICsKICBnZW9tX3BvaW50KGFscGhhPTAuNjUsIGFlcyhzaXplPUNvdW50KSkgKyAKICBnZW9tX2xpbmUoYWxwaGE9MC4yNSkgKwogIGZhY2V0X2dyaWQoZmFjdG9yKGdzdWIoIlxcICIsIlxuIixwaGVfY2VudHJlKSwgbGV2ZWxzPWdzdWIoIlxcICIsIlxuIixQSEUucmVnaW9uLmNvbHMuYnJldyRVS0hTQS5yZWdpb24pKX4uLCBzd2l0Y2g9J3knKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgdGhlbWUoc3RyaXAucGxhY2VtZW50ID0gIm91dHNpZGUiKSArCiAgdGhlbWUoc3RyaXAuYmFja2dyb3VuZCA9IGVsZW1lbnRfcmVjdChjb2xvcj0nd2hpdGUnLCBmaWxsPSd3aGl0ZScsbGluZXR5cGU9InNvbGlkIiksIHN0cmlwLnRleHQueT1lbGVtZW50X3RleHQoY29sb3IgPSAiZ3JleTI1IixhbmdsZT0wLCBzaXplPTUpKSArIAogIHNjYWxlX3NpemVfYXJlYShtYXhfc2l6ZSA9IDQuNSxicmVha3M9YygxLDUsMTAsMjAsMzAsNDApKSArCiAgdGhlbWUudGV4dC5zaXplICsKICBzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLmNvbHMsIGJyZWFrcz1zdWJsaW5lYWdlcy5jb2xzLmJyZXckc3VibGluZWFnZSkgKyAKICBsYWJzKHk9IlJlZ2lvbiIsIHg9IlllYXIiLCBjb2xvcj0iU3VibGluZWFnZSIpIAogCnAuUEhFLm1ham9yLnN1YmxpbmVhZ2UuUEhFY2VudHJlLmRhdGUuYnViYmxlcGxvdAoKYGBgClwKRG8gc29tZSBzcGVjaWZpYyBhbmFseXNpcyBmb3IgdGhlIDMgTm9ydGhlcm4gcmVnaW9ucwpgYGB7ciwgZmlnLndpZHRoPTQsIGZpZy5oZWlnaHQ9M30KIyBHZW5lcmF0ZSBzb21lIHN0YXRzIGJ5IFBIRSBSZWdpb24KIFBIRS5tZXRhZGF0YS5saW5rZWQgJT4lIAogIGRwbHlyOjpmaWx0ZXIocGhlX2NlbnRyZSAlaW4lIGMoIk5vcnRoIEVhc3QiLCAiTm9ydGggV2VzdCIsICJZb3Jrc2hpcmUgYW5kIEh1bWJlciIpKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50PW4oKSkKCiBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6ZmlsdGVyKHBoZV9jZW50cmUgJWluJSBjKCJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KHllYXIpICU+JQogIGRwbHlyOjpzdW1tYXJpc2UoY291bnQ9bigpKQoKCnAuUEhFLm1ham9yLnN1YmxpbmVhZ2UuM05vcnRoZXJuUmVnaW9ucyA8LSBQSEUubWV0YWRhdGEubGlua2VkICU+JSAKICBkcGx5cjo6ZmlsdGVyKHBoZV9jZW50cmUgJWluJSBjKCJOb3J0aCBFYXN0IiwgIk5vcnRoIFdlc3QiLCAiWW9ya3NoaXJlIGFuZCBIdW1iZXIiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCB5ZWFyLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50PW4oKSkgJT4lCiAgZ2dwbG90KGFlcyh5ZWFyLCBDb3VudCwgZmlsbD1waGVfY2VudHJlKSkgKyAKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHdpZHRoPTAuNjUpICsKICBzY2FsZV9maWxsX21hbnVhbCh2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbikgKwogIHRoZW1lX2J3KCkgKyB0aGVtZS50ZXh0LnNpemUgKwogIHNjYWxlX3hfY29udGludW91cyhicmVha3M9c2VxKDIwMTIsMjAxOCwxKSkgKwogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9cHJldHR5KSArCiAgbGFicyh0aXRsZT0iU2FtcGxlcyBpbiAzIE5vcnRoZXJuIFJlZ2lvbnMiLCB4PSJDb2xsZWN0aW9uIFllYXIiLCB5PSJTYW1wbGUgQ291bnQiLCBmaWxsPSJQdWJsaWMgSGVhbHRoXG5SZWdpb24iKSArCiAgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICAjZ2VvbV90ZXh0KGFlcyh4PXllYXIseT1Db3VudC0wLjUsIGxhYmVsPUNvdW50KSwgY29sb3I9J2dyZXk5NScsIHNpemU9dGhlbWUudGV4dC5zaXplLndpdGhpbikgKwogIE5VTEwKcC5QSEUubWFqb3Iuc3VibGluZWFnZS4zTm9ydGhlcm5SZWdpb25zCgpgYGAKCgoKXApTaW5nbGUgbGlua2FnZSBuZXR3b3JrIG9mIGlkZW50aWNhbCBnZW5vbWVzIGZyb20gVUsKCmBgYHtyfQojIENvbnN0cmFpbiBieSBTTlAgZGlzdGFuY2UgKGlkZW50aWNhbCBpbiB0aGUgYXNyIHNucCB0cmVlKQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGFbUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhJERpc3RhbmNlLlBoeWxvPT0wLF0KCiMgYW5kIGEgbWF4IG9mIDIgeWVhcnMKI1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzW1BIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZTw9MixdCgoKIyBBbmQgbWFrZSBzdXJlIHRoYXQgd2UgYWN0dWFsbHkgaGF2ZSBnZW5ldGljIGRpc3RhbmNlIGRhdGEgZm9yIGFsbCBzYW1wbGVzIHdpdGhpbiB0aGUgbmV0d29yawpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1shaXMubmEoUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMkRGlzdGFuY2UuUGh5bG8pLF0KCiMgcmVtb3ZlIHNlbGYtc2FtcGxlcwpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1tQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2FscyRzYW1lLnNhbXBsZT09ImRpZmZlcmVudCIsXQoKCiMgY2xlYW51cCBzb21lIGRhdGEgbm9pc2UKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMgPC0gUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHNbIWlzLm5hKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzJHllYXIudDEpLF0KCiMgcHJlcGFyZSBpbnRwdXQgZGF0YSAod2l0aCBlZGdlIGluZm8pClBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSA8LSBQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fsc1ssYygiVGF4YTEiLCJUYXhhMiIsIkRpc3RhbmNlLlBoeWxvIiwiZGVjaW1hbC5kYXRlLmRpc3RhbmNlIiwieWVhci5kaXN0YW5jZSIsIk9yaWVudGF0aW9uLkNsYXNzIiwiZXBpLnRpbWUuZGlzdGFuY2UuY2F0LnllYXJzIiwiZXBpLnRpbWUuZGlzdGFuY2UuY2F0IildCgojIyMjIyMjIyMjIyMKIyBzb21lIGlzc3VlcyB3aXRoIHVwZGF0ZSB0byBSNCAtIGRvdWJsZSBzaWRlZCBtYXRyaXgKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJGVkZ2VuYW1lIDwtIHNhcHBseSgxOm5yb3coUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxKSwgZnVuY3Rpb24oeCkgcGFzdGUwKHNvcnQoYXMuY2hhcmFjdGVyKHVubGlzdChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDFbeCxjKCJUYXhhMSIsIlRheGEyIildKSkpLGNvbGxhcHNlPSJfX18iKSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxIDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MVshZHVwbGljYXRlZChQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkZWRnZW5hbWUpLF0KCiMgQWxzbyBoYXZpbmcgYW4gaXNzdWUgd2l0aCB0YXhhIGFzIGZhY3RvcnMgaGVyZQpQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkVGF4YTEgPC0gYXMuY2hhcmFjdGVyKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSRUYXhhMSkKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJFRheGEyIDwtIGFzLmNoYXJhY3RlcihQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkVGF4YTIpCiMjIyMjIyMjIyMjIwojIERlZHVwbGljYXRlCgojaW52ZXJzZSB3ZWlnaHQKUEhFLmFsaWdubWVudC5kYXRhLmRpc3QubWVsdC5tZXRhLmlkZW50aWNhbHMuaW5wdXQxJGRlY2ltYWwuZGF0ZS5kaXN0YW5jZS5pbnYgPC0gMS8xLyhQSEUuYWxpZ25tZW50LmRhdGEuZGlzdC5tZWx0Lm1ldGEuaWRlbnRpY2Fscy5pbnB1dDEkZGVjaW1hbC5kYXRlLmRpc3RhbmNlKzAuMDQpCgojIE1ha2UgYWN0dWFsIG5ldHdvcmsKc2V0LnNlZWQoMTIzNikKUEhFLmlkZW50aWNhbHMubmV0d29yayA8LSBuZXR3b3JrKFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YS5pZGVudGljYWxzLmlucHV0MSwgbWF0cml4LnR5cGUgPSAiZWRnZWxpc3QiLCBpZ25vcmUuZXZhbCA9IEZBTFNFLCBkaXJlY3RlZCA9IEYsIGxvb3BzID0gRikKCiNQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnIDwtIGdnbmV0d29yayhQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLCBsYXlvdXQgPSAia2FtYWRha2F3YWkiLCB3ZWlnaHRzID0gImRlY2ltYWwuZGF0ZS5kaXN0YW5jZS5pbnYiKQojUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyA8LSBnZ25ldHdvcmsoUEhFLmlkZW50aWNhbHMubmV0d29yaywgbGF5b3V0ID0gImZydWNodGVybWFucmVpbmdvbGQiLCB3ZWlnaHRzID0gImRlY2ltYWwuZGF0ZS5kaXN0YW5jZSIpClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgPC0gZ2duZXR3b3JrKFBIRS5pZGVudGljYWxzLm5ldHdvcmssIGxheW91dCA9ICJmcnVjaHRlcm1hbnJlaW5nb2xkIikKClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckVGF4YTEgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyR2ZXJ0ZXgubmFtZXMKCiMgZXh0cmFjdCB0ZW1wb3JhbCBjbHVzdGVycyBmcm9tIG5ldHdvcmsKUEhFLmlkZW50aWNhbHMubmV0d29yay5pZyA8LSBhc0lncmFwaChQSEUuaWRlbnRpY2Fscy5uZXR3b3JrKQpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMgPC0gZGF0YS5mcmFtZShUYXhhMT1uZXR3b3JrLnZlcnRleC5uYW1lcyhQSEUuaWRlbnRpY2Fscy5uZXR3b3JrKSwgdmVydGV4Lm5vPWFzLnZlY3RvcihWKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuaWcpKSwgY2x1c3Rlcj1pZ3JhcGg6OmNvbXBvbmVudHMoUEhFLmlkZW50aWNhbHMubmV0d29yay5pZykkbWVtYmVyc2hpcCkKUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIgPC0gcGFzdGUwKCJDbHVzdGVyIixQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkY2x1c3RlcikKCiMgbWVyZ2UgbWV0YWRhdGEgYmFjayBpbgpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnIDwtIHBseXI6OmpvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5nZywgZGF0YS5mcmFtZShUYXhhMT1QSEUubWV0YWRhdGEubGlua2VkJFNhbXBsZV9OYW1lLCBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIHN0cmluZ3NBc0ZhY3RvcnMgPSBGKSxieT0iVGF4YTEiLCB0eXBlPSJsZWZ0IikKClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgPC0gcGx5cjo6am9pbihQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLCBkYXRhLmZyYW1lKFRheGExPVBIRS5pZGVudGljYWxzLm5ldHdvcmsuY29tcG9uZW50cyRUYXhhMSwgQ2x1c3Rlcj1QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkQ2x1c3RlciksIGJ5PSJUYXhhMSIsIHR5cGU9ImxlZnQiKQoKCiMgCiMgQWRkIHRlbXBvcmFsIGNvbG91ciBzY2FsZQojdW5pcXVlKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2ckZXBpLnRpbWUuZGlzdGFuY2UuY2F0KQoKZXBpLnRpbWUuZGlzdGFuY2UuY2F0LmNvbHMgPC0gcmV2KGNvbG9yUmFtcFBhbGV0dGUoYnJld2VyLnBhbCg4LCAiR3JleXMiKSkobGVuZ3RoKHVuaXF1ZShQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnJGVwaS50aW1lLmRpc3RhbmNlLmNhdCkpLTEpKQoKCiMgUGxvdCBuZXR3b3JrCnAuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QIDwtIGdncGxvdChQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLCBhZXMoeCA9IHgsIHkgPSB5LCB4ZW5kID0geGVuZCwgeWVuZCA9IHllbmQpKSArIAogIGdlb21fZWRnZXMoYWxwaGE9MC45MCwgY3VydmF0dXJlID0gMC4yLCBhZXMoY29sb3I9ZmFjdG9yKGVwaS50aW1lLmRpc3RhbmNlLmNhdCksIGxpbmV0eXBlPWZhY3RvcihlcGkudGltZS5kaXN0YW5jZS5jYXQpKSkgKwogICNzY2FsZV9jb2xvcl9tYW51YWwodmFsdWVzPWMoImdyZXk1IiwiZ3JleTM1IiwiZ3JleTU1IiwgImdyZXk2NSIsICJncmV5NzUiKSwgbmFtZT0iU05QXG5EaXN0YW5jZSIpICsKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iVGVtcG9yYWxcbkRpc3RhbmNlIiwgdmFsdWVzID0gZXBpLnRpbWUuZGlzdGFuY2UuY2F0LmNvbHMpICsKICBzY2FsZV9saW5ldHlwZShuYW1lPSJUZW1wb3JhbFxuRGlzdGFuY2UiKSArCiAgdGhlbWVfYmxhbmsoKSArCiAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2NvbG9yKCkgKyBnZ25ld3NjYWxlOjpuZXdfc2NhbGUoInNpemUiKSArCiAgI2dlb21fbm9kZWxhYmVsKGFlcyhjb2xvcj1nZW5kZXJfb3JpZW50YXRpb24sIGxhYmVsPXBhc3RlKFRheGExLHllYXIsc2VwPSJcbiIpLGZvbnRmYWNlID0gImJvbGQiKSwgYWxwaGE9MC44LCBzaXplPXRoZW1lLnRleHQuc2l6ZS53aXRoaW4tMC40LCBsYWJlbC5zaXplPTAuMTUsIGxhYmVsLnBhZGRpbmcgPSB1bml0KDAuMDUsICJsaW5lcyIpKSArCiAgZ2VvbV9ub2RlcyhzaXplPTIuNSwgYWVzKGNvbG9yPWdlbmRlcl9vcmllbnRhdGlvbiksIGFscGhhPTAuOSkgKyAKICBzY2FsZV9jb2xvcl9tYW51YWwobmFtZT0iR2VuZGVyXG5PcmllbnRhdGlvbiIsIHZhbHVlcz1QSEUub3JpZW50YXRpb24uY29scyRvcmllbnRhdGlvbi5jb2xzLCBicmVha3M9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24pICsgCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBOVUxMCnAuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QCgpgYGAKClBsb3QgdGhpcyBhZ2FpbnN0IGEgVUsgdHJlZT8KYGBge3J9CmdoZWF0bWFwKGdndHJlZShUUEEucHlqYXIudHJlZS5zdWJzZXQudWspLApkYXRhLmZyYW1lKHJvdy5uYW1lcz1QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHMkVGF4YTEsIENsdXN0ZXI9UEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzJENsdXN0ZXIpKQoKYGBgCgoKClwKU29tZSBzdGF0cyBmcm9tIHRoaXMKYGBge3J9CnAuUEhFLmlkZW50aWNhbC5PcmllbnRhdGlvbl9jbGFzcy5ieWRhdGVkaXN0IDwtIFBIRS5hbGlnbm1lbnQuZGF0YS5kaXN0Lm1lbHQubWV0YSAlPiUKICBkcGx5cjo6ZmlsdGVyKHNhbWUuc2FtcGxlPT0iZGlmZmVyZW50IiwgRGlzdGFuY2UuUGh5bG89PTApICU+JQogICNmaWx0ZXIoZGVjaW1hbC5kYXRlLmRpc3RhbmNlPD0xKSAlPiUKICBkcGx5cjo6Z3JvdXBfYnkoZXBpLnRpbWUuZGlzdGFuY2UuY2F0LCBPcmllbnRhdGlvbi5DbGFzcykgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuY2xhc3MuZGF0ZT1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoc3VtLmNsYXNzPXN1bShDb3VudC5jbGFzcy5kYXRlKSwgZnJhY3QuY2xhc3M9Q291bnQuY2xhc3MuZGF0ZS9zdW0uY2xhc3MpICU+JQogIGdncGxvdChhZXMoeD1lcGkudGltZS5kaXN0YW5jZS5jYXQsIHk9Q291bnQuY2xhc3MuZGF0ZSwgZmlsbD1PcmllbnRhdGlvbi5DbGFzcykpICsKICBnZW9tX2JhcihzdGF0PSdpZGVudGl0eScsIHBvc2l0aW9uPSdzdGFjaycpICsKICB0aGVtZV9idygpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iVGltZSBiZXR3ZWVuIHNhbXBsZXMiLCB5PSJJbnRlcmFjdGlvbiBDb3VudCIsIGZpbGw9Ik9yaWVudGF0aW9uIFR5cGUiKQpwLlBIRS5pZGVudGljYWwuT3JpZW50YXRpb25fY2xhc3MuYnlkYXRlZGlzdAoKCiAgCnAuUEhFLmlkZW50aWNhbC5PcmllbnRhdGlvbl9jbGFzcy5ieVplcm9kaXN0LmNsdXN0ZXIgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyAlPiUKICBkcGx5cjo6ZmlsdGVyKCFpcy5uYShPcmllbnRhdGlvbi5DbGFzcykpICU+JQogIGRwbHlyOjpncm91cF9ieShDbHVzdGVyLCBPcmllbnRhdGlvbi5DbGFzcykgJT4lIAogIGRwbHlyOjpzdW1tYXJpc2UoQ291bnQuY2xhc3MuY2x1c3Rlcj1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoc3VtLmNsYXNzPXN1bShDb3VudC5jbGFzcy5jbHVzdGVyKSwgZnJhY3QuY2xhc3M9Q291bnQuY2xhc3MuY2x1c3Rlci9zdW0uY2xhc3MpICU+JQogIGRwbHlyOjphcnJhbmdlKGRlc2Moc3VtLmNsYXNzKSkgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6bXV0YXRlKENsdXN0ZXI9YXNfZmFjdG9yKENsdXN0ZXIpKSAlPiUKICBnZ3Bsb3QoYWVzKHg9Q2x1c3RlciwgeT1Db3VudC5jbGFzcy5jbHVzdGVyLCBmaWxsPU9yaWVudGF0aW9uLkNsYXNzKSkgKwogIGdlb21fYmFyKHN0YXQ9J2lkZW50aXR5JywgcG9zaXRpb249J3N0YWNrJykgKyAKICB0aGVtZV9idygpICsKICB4LnRoZW1lLmF4aXMucm90YXRlICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iSW50ZXJhY3Rpb24gQ291bnQiLCBmaWxsPSJPcmllbnRhdGlvbiBUeXBlIikKcC5QSEUuaWRlbnRpY2FsLk9yaWVudGF0aW9uX2NsYXNzLmJ5WmVyb2Rpc3QuY2x1c3RlcgoKZC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciA8LSBsZWZ0X2pvaW4oUEhFLmlkZW50aWNhbHMubmV0d29yay5jb21wb25lbnRzWyxjKCJUYXhhMSIsIkNsdXN0ZXIiKV0sIFBIRS5tZXRhZGF0YS5saW5rZWRbLGMoIlNhbXBsZV9OYW1lIiwicGhlX2NlbnRyZSIsImxvbmRvbiIsInllYXIiLCJhZ2VfZ3JvdXAiLCJ1a2Jvcm4iLCJnZW5kZXJfb3JpZW50YXRpb24iLCJoaXZwb3MiLCJUUEEucGluZWNvbmUuc3VibGluZWFnZSIsIlRQQV9MaW5lYWdlIildLCBieT1jKCJUYXhhMSI9IlNhbXBsZV9OYW1lIikpICU+JQogIGRwbHlyOjpncm91cF9ieShUUEEucGluZWNvbmUuc3VibGluZWFnZSwgQ2x1c3RlciwgZ2VuZGVyX29yaWVudGF0aW9uKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50Lm9yaWVudC5jbHVzdGVyPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb3VudC5jbHVzdGVyPXN1bShjb3VudC5vcmllbnQuY2x1c3RlciksIGZyYWN0PWNvdW50Lm9yaWVudC5jbHVzdGVyL2NvdW50LmNsdXN0ZXIpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb3VudC5jbHVzdGVyKSkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm89YXNfZmFjdG9yKENsdXN0ZXIpKQoKCiMgUGxvdCBzYW1wbGUgY291bnRzIGJ5IGdlbm9tZSBjbHVzdGVyIChjb2xvdXJlZCBieSBvcmllbnRhdGlvbikKcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciA8LSBkLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JQogIGdncGxvdChhZXMoQ2x1c3Rlci5vLCBjb3VudC5vcmllbnQuY2x1c3RlciwgZmlsbD1nZW5kZXJfb3JpZW50YXRpb24pKSArIAogIGdlb21fYmFyKHN0YXQ9ImlkZW50aXR5Iiwgd2lkdGg9MC42NSkgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IkdlbmRlclxuT3JpZW50YXRpb24iLCB2YWx1ZXM9UEhFLm9yaWVudGF0aW9uLmNvbHMkb3JpZW50YXRpb24uY29scywgYnJlYWtzPVBIRS5vcmllbnRhdGlvbi5jb2xzJG9yaWVudGF0aW9uLCBndWlkZSA9IGd1aWRlX2xlZ2VuZChvcmRlciA9IDEpKSArCiAgdGhlbWVfbGlnaHQoKSArCiAgeC50aGVtZS5heGlzLnJvdGF0ZSArIAogIHNjYWxlX3lfY29udGludW91cyhicmVha3M9c2VxKDAsNDUsNSkpICsKICB0aGVtZS50ZXh0LnNpemUgKyB0aGVtZShsZWdlbmQua2V5LnNpemUgPSB1bml0KDAuNTUsImxpbmUiKSxsZWdlbmQucG9zaXRpb249J3JpZ2h0JykgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iU2FtcGxlIENvdW50IiwgZmlsbD0iUGF0aWVudCBHZW5kZXIgT3JpZW50YXRpb24iKSAKCiMgQWRkIGRldGFpbHMgb2Ygc3VibGluZWFnZSAgCnAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgPC0gcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciArIAogIGdnbmV3c2NhbGU6Om5ld19zY2FsZV9jb2xvcigpICsKICBnZW9tX3BvaW50KGRhdGE9KGQuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgJT4lIHNlbGVjdChDbHVzdGVyLm8sIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSAlPiUgZGlzdGluY3QoKSksIGFlcyhDbHVzdGVyLm8sIC0xLjUsIGNvbG9yPVRQQS5waW5lY29uZS5zdWJsaW5lYWdlKSwgaW5oZXJpdC5hZXMgPSBGKSArIHNjYWxlX2NvbG9yX21hbnVhbCh2YWx1ZXM9c3VibGluZWFnZXMuY29scy5icmV3JHN1YmxpbmVhZ2UuY29scywgYnJlYWtzPXN1YmxpbmVhZ2VzLmNvbHMuYnJldyRzdWJsaW5lYWdlLCBuYW1lPSJTdWJsaW5lYWdlIiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAyKSkgKwogIE5VTEwKCiMgQWRkIGEgc3VibGluZWFnZSBheGlzIGxhYmVsIChiaXQgb2YgYSBoYWNrKQpwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIHAuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgKyAKICBnZW9tX3RleHQoZGF0YT1kYXRhLmZyYW1lKGxhYj0iU3VibGluZWFnZSIsIHk9LTEuNSwgeD0yOCwgc3RyaW5nc0FzRmFjdG9ycz1GKSwgYWVzKGxhYmVsPWxhYiwgeD14LCB5PXkpLCBoanVzdCA9IDAuMSwgc2l6ZT10aGVtZS50ZXh0LnNpemUud2l0aGluLCBpbmhlcml0LmFlcyA9IEYpICsKICBjb29yZF9jYXJ0ZXNpYW4oeD1jKDEsIDI3KSwgY2xpcD0nb2ZmJykKICAKcC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlcgoKI2dnc2F2ZShwYXN0ZTAoRmlndXJlX291dHB1dF9kaXJlY3RvcnksIlN1cEZpZzZfSWRlbnRpY2FsLVNOUC1jbHVzdF9vcmllbnRhdGlvbi4iLGZvcm1hdChTeXMuRGF0ZSgpLCIlWSVtJWQiKSwiLnBkZiIpLCB1bml0cz0nbW0nLCB3aWR0aD0xMjAsIGhlaWdodD0xMDAsIGRldmljZT0ncGRmJywgZHBpPTEyMDApCgpgYGAKXApQb3NzaWJsZSB0byBpbnRyb2R1Y2Ugc29tZSBtb3JlIGluZm8gaW50byB0aGF0IHBsb3Q/CgoKYGBge3J9CmQuUEhFLmlkZW50aWNhbC5yZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIGxlZnRfam9pbihQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmNvbXBvbmVudHNbLGMoIlRheGExIiwiQ2x1c3RlciIpXSwgUEhFLm1ldGFkYXRhLmxpbmtlZFssYygiU2FtcGxlX05hbWUiLCJwaGVfY2VudHJlIiwibG9uZG9uIiwieWVhciIsImFnZV9ncm91cCIsInVrYm9ybiIsImdlbmRlcl9vcmllbnRhdGlvbiIsImhpdnBvcyIsIlRQQS5waW5lY29uZS5zdWJsaW5lYWdlIiwiVFBBX0xpbmVhZ2UiKV0sIGJ5PWMoIlRheGExIj0iU2FtcGxlX05hbWUiKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBDbHVzdGVyLCBwaGVfY2VudHJlKSAlPiUKICBkcGx5cjo6c3VtbWFyaXNlKGNvdW50LnJlZ2lvbi5jbHVzdGVyPW4oKSkgJT4lCiAgZHBseXI6Om11dGF0ZShjb3VudC5jbHVzdGVyPXN1bShjb3VudC5yZWdpb24uY2x1c3RlciksIGZyYWN0PWNvdW50LnJlZ2lvbi5jbHVzdGVyL2NvdW50LmNsdXN0ZXIpICU+JQogIGRwbHlyOjp1bmdyb3VwKCkgJT4lCiAgZHBseXI6OmFycmFuZ2UoZGVzYyhjb3VudC5jbHVzdGVyKSkgJT4lCiAgZHBseXI6Om11dGF0ZShDbHVzdGVyLm89YXNfZmFjdG9yKENsdXN0ZXIpKQoKCnAuUEhFLmlkZW50aWNhbC5SZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyIDwtIGQuUEhFLmlkZW50aWNhbC5yZWdpb24uYnlaZXJvZGlzdC5jbHVzdGVyICU+JQogIGdncGxvdChhZXMoQ2x1c3Rlci5vLCBjb3VudC5yZWdpb24uY2x1c3RlciwgZmlsbD1waGVfY2VudHJlKSkgKyAKICBnZW9tX2JhcihzdGF0PSJpZGVudGl0eSIsIHdpZHRoPTAuNjUsIHBvc2l0aW9uPSdmaWxsJykgKwogIHNjYWxlX2ZpbGxfbWFudWFsKG5hbWU9IlVLSFNBXG5SZWdpb24iLCB2YWx1ZXM9UEhFLnJlZ2lvbi5jb2xzLmJyZXckcmVnaW9uLmNvbCwgYnJlYWtzPVBIRS5yZWdpb24uY29scy5icmV3JFVLSFNBLnJlZ2lvbiwgZ3VpZGUgPSBndWlkZV9sZWdlbmQob3JkZXIgPSAxKSkgKwogIHRoZW1lX2xpZ2h0KCkgKwogIHgudGhlbWUuYXhpcy5yb3RhdGUgKyAKICBzY2FsZV95X2NvbnRpbnVvdXMoYnJlYWtzPXNlcSgwLDQ1LDUpKSArCiAgdGhlbWUudGV4dC5zaXplICsgdGhlbWUobGVnZW5kLmtleS5zaXplID0gdW5pdCgwLjU1LCJsaW5lIiksbGVnZW5kLnBvc2l0aW9uPSdyaWdodCcpICsKICBndWlkZXMoZmlsbD1ndWlkZV9sZWdlbmQobmNvbD0yKSkgKwogIGxhYnMoeD0iSWRlbnRpY2FsIEdlbm9tZSBDbHVzdGVyIiwgeT0iUmVnaW9uIFByb3BvcnRpb24iLCBmaWxsPSJVS0hTQSBSZWdpb24iKSAKCgpgYGAKCmBgYHtyLCBmaWcuaGVpZ2h0PTgsIGZpZy53aWR0aD04fQpwLlBIRS5pZGVudGljYWwuYnlaZXJvZGlzdC5jbHVzdGVyLmJhcmNvbWJpIDwtIHBsb3RfZ3JpZChwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICsgeC50aGVtZS5zdHJpcCwgcC5QSEUuaWRlbnRpY2FsLlJlZ2lvbi5ieVplcm9kaXN0LmNsdXN0ZXIsIG5jb2w9MSwgYXhpcz0icmx0IiwgYWxpZ249VCwgcmVsX2hlaWdodHMgPSBjKDIsMSksIGxhYmVscz1jKCJCIiwiQyIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQoKI3AuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmkKI3AuUEhFLmlkZW50aWNhbHMubmV0d29yay4wU05QCgpwbG90X2dyaWQocC5QSEUuaWRlbnRpY2Fscy5uZXR3b3JrLjBTTlAsIHAuUEhFLmlkZW50aWNhbC5ieVplcm9kaXN0LmNsdXN0ZXIuYmFyY29tYmksIG5jb2w9MSwgcmVsX2hlaWdodHM9YygyLDMpLCBsYWJlbHM9YygiQSIsIiIpLCBsYWJlbF9zaXplPXBhbmVsLmxhYi5zaXplKQpgYGAKCgoKYGBge3J9ClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cucmVnaW9uLnNjYXR0ZXJwaWUuZ3JvdXBzIDwtIFBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cgJT4lCiAgZHBseXI6OnNlbGVjdChDbHVzdGVyLCBUYXhhMSwgcGhlX2NlbnRyZSkgJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KENsdXN0ZXIsIHBoZV9jZW50cmUpICU+JSAKICBkcGx5cjo6c3VtbWFyaXNlKENvdW50LmNlbnRyZT1uKCkpICU+JQogIGRwbHlyOjptdXRhdGUoeD1DbHVzdGVyLCB5PTMuNSkgJT4lCiAgcGl2b3Rfd2lkZXIobmFtZXNfZnJvbT0icGhlX2NlbnRyZSIsIHZhbHVlc19mcm9tPSJDb3VudC5jZW50cmUiLCB2YWx1ZXNfZmlsbD0wKSAlPiUKICBkcGx5cjo6c2VsZWN0KENsdXN0ZXIseCx5LHVuaXF1ZShQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnJHBoZV9jZW50cmUpKSAlPiUKICBkcGx5cjo6dW5ncm91cCgpICU+JQogIGRwbHlyOjptdXRhdGUoQ2x1c3Rlci5udW1lcmljPWFzLm51bWVyaWMoMToyNykpCiAgCgpwLlBIRS5pZGVudGljYWwuR2VuZGVyT3JpZW50YXRpb24uYnlaZXJvZGlzdC5jbHVzdGVyICsgCiAgZ2duZXdzY2FsZTo6bmV3X3NjYWxlX2ZpbGwoKSAjKwogIAoKYGBgCgoKClwKR2V0IGEgZmV3IG1vcmUgc3RhdHMgb24gdGhlIGxhcmdlc3QgY2x1c3RlciAoQ2x1c3RlciA4KQpgYGB7cn0KI2QuUEhFLmlkZW50aWNhbC5HZW5kZXJPcmllbnRhdGlvbi5ieVplcm9kaXN0LmNsdXN0ZXIgJT4lIGZpbHRlcihDbHVzdGVyPT0iQ2x1c3RlcjgiKQoKUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5pZGVudGljYWwuY2x1c3RlcjggPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyAlPiUgZmlsdGVyKENsdXN0ZXI9PSJDbHVzdGVyOCIpICAlPiUKICBzZWxlY3QodmVydGV4Lm5hbWVzLCBPcmllbnRhdGlvbi5DbGFzcywgcGhlX2NlbnRyZSwgeWVhciwgVFBBX0xpbmVhZ2UsIFRQQS5waW5lY29uZS5zdWJsaW5lYWdlLCBoaXZwb3MsIENsdXN0ZXIpCgpzb3J0KHVuaXF1ZShQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbC5jbHVzdGVyOCR5ZWFyKSkKCmBgYAoKXApHZXQgc29tZSBtb3JlIGluZm9ybWF0aW9uIGFib3V0IHRoZSBoZXRlcm9zZXh1YWwgb25seSBjbHVzdGVycwpgYGB7cn0KUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5pZGVudGljYWxfaGV0ZXJvY2x1c3RlcnMgPC0gUEhFLmlkZW50aWNhbHMubmV0d29yay5nZyAlPiUgZmlsdGVyKENsdXN0ZXIgJWluJSBjKCJDbHVzdGVyMTIiLCAiQ2x1c3RlcjIwIiwgIkNsdXN0ZXIyNyIpKSAgJT4lCiAgc2VsZWN0KHZlcnRleC5uYW1lcywgQ2x1c3RlciwgZ2VuZGVyX29yaWVudGF0aW9uLCBwaGVfY2VudHJlLCB5ZWFyLCBUUEFfTGluZWFnZSwgVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UsIGhpdnBvcykgJT4lIAogIGRpc3RpbmN0KCkgJT4lCiAgYXJyYW5nZShDbHVzdGVyLCB5ZWFyLCBnZW5kZXJfb3JpZW50YXRpb24pCgpQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnLmlkZW50aWNhbF9oZXRlcm9jbHVzdGVycwpgYGAKXCAKQW5kIGRvIHRoZSBzYW1lIGZvciB0aGUgc21hbGwgbWl4ZWQvR0JNU00gY2x1c3RlcnMKYGBge3J9ClBIRS5pZGVudGljYWxzLm5ldHdvcmsuZ2cuaWRlbnRpY2FsX25vdC5oZXRlcm9jbHVzdGVycyA8LSBQSEUuaWRlbnRpY2Fscy5uZXR3b3JrLmdnICU+JSBmaWx0ZXIoQ2x1c3RlciAlbm90aW4lIGMoIkNsdXN0ZXIxMiIsICJDbHVzdGVyMjAiLCAiQ2x1c3RlcjI3IiwgIkNsdXN0ZXI4IikpICAlPiUKICBzZWxlY3QodmVydGV4Lm5hbWVzLCBDbHVzdGVyLCBnZW5kZXJfb3JpZW50YXRpb24sIHBoZV9jZW50cmUsIHllYXIsIFRQQV9MaW5lYWdlLCBUUEEucGluZWNvbmUuc3VibGluZWFnZSwgaGl2cG9zKSAlPiUgCiAgZGlzdGluY3QoKSAlPiUKICBhcnJhbmdlKENsdXN0ZXIsIHllYXIsIGdlbmRlcl9vcmllbnRhdGlvbikKUEhFLmlkZW50aWNhbHMubmV0d29yay5nZy5pZGVudGljYWxfbm90LmhldGVyb2NsdXN0ZXJzCmBgYAoKCgpXaGF0IHByb3BvcnRpb24gb2YgaGV0ZXJvc2V4dWFscyBoYXZlIGFuIGlkZW50aWNhbCBHQk1TTSBwYWlyZWQgZ2Vub21lPwpcCmBgYHtyfQoKIyBEZWxpbmVhdGUgaGV0ZXJvc2V4dWFsIGNsdXN0ZXJzCmQuUEhFLmlkZW50aWNhbC5oZXRlcm9zZXh1YWwuY2x1c3RlcnMgPC0gZC5QSEUuaWRlbnRpY2FsLkdlbmRlck9yaWVudGF0aW9uLmJ5WmVyb2Rpc3QuY2x1c3RlciAlPiUgCiAgZHBseXI6Om11dGF0ZShpcy5oZXRlcm9zZXh1YWw9aWZlbHNlKGdlbmRlcl9vcmllbnRhdGlvbiVpbiUgYygiTVNXIiwgIldTTSIpLCAiaGV0ZXJvc2V4dWFsIiwgaWZlbHNlKGdlbmRlcl9vcmllbnRhdGlvbj09IkdCTVNNIiwiR0JNU00iLCAiVW5rbm93biIpKSkgJT4lCiAgZHBseXI6Omdyb3VwX2J5KENsdXN0ZXIsaXMuaGV0ZXJvc2V4dWFsKSAlPiUgCiAgZHBseXI6Om11dGF0ZShjb3VudC5oZXRlcm89c3VtKGNvdW50Lm9yaWVudC5jbHVzdGVyKSwgZnJhY3QuaGV0ZXJvPXN1bShjb3VudC5vcmllbnQuY2x1c3RlcikvY291bnQuY2x1c3RlcikgJT4lCiAgZHBseXI6OnVuZ3JvdXAoKSAlPiUKICBkcGx5cjo6ZmlsdGVyKGlzLmhldGVyb3NleHVhbD09ImhldGVyb3NleHVhbCIpICU+JSAKICBkcGx5cjo6c2VsZWN0KC1jKGNvdW50Lm9yaWVudC5jbHVzdGVyLCBnZW5kZXJfb3JpZW50YXRpb24sIGZyYWN0KSkgJT4lCiAgZHBseXI6OmRpc3RpbmN0KCkgJT4lCiAgZHBseXI6Om11dGF0ZShjbHVzdGVyLnR5cGU9aWZlbHNlKGZyYWN0LmhldGVybz09MSwgImhldGVyby5vbmx5IiwgIm90aGVyIikpCgpkLlBIRS5pZGVudGljYWwuaGV0ZXJvc2V4dWFsLmNsdXN0ZXJzIAoKIyBXaGF0IHByb3BvcnRpb24gb2YgaGV0ZXJvc2V4dWFscyAobj0yMCkgYXJlIGluIGEgaGV0ZXJvc2V4dWFsLW9ubHkgY2x1c3Rlcj8KZC5QSEUuaWRlbnRpY2FsLmhldGVyb3NleHVhbC5jbHVzdGVycyAlPiUgCiAgZHBseXI6Omdyb3VwX2J5KGNsdXN0ZXIudHlwZSkgJT4lCiAgZHBseXI6OnN1bW1hcmlzZShjb3VudC5pbi5oZXRlcm8uY2x1c3Rlcj1zdW0oY291bnQuaGV0ZXJvKSkgJT4lIAogIGRwbHlyOjptdXRhdGUoZnJhY3QuaW4uaGV0ZXJvPWNvdW50LmluLmhldGVyby5jbHVzdGVyL3N1bShjb3VudC5pbi5oZXRlcm8uY2x1c3RlcikpCiAgCgojbGVmdF9qb2luKFBIRS5pZGVudGljYWxzLm5ldHdvcmsuY29tcG9uZW50c1ssYygiVGF4YTEiLCJDbHVzdGVyIildLCBQSEUubWV0YWRhdGEubGlua2VkWyxjKCJTYW1wbGVfTmFtZSIsInBoZV9jZW50cmUiLCJsb25kb24iLCJ5ZWFyIiwiYWdlX2dyb3VwIiwidWtib3JuIiwiZ2VuZGVyX29yaWVudGF0aW9uIiwiaGl2cG9zIiwiVFBBLnBpbmVjb25lLnN1YmxpbmVhZ2UiLCJUUEFfTGluZWFnZSIpXSwgYnk9YygiVGF4YTEiPSJTYW1wbGVfTmFtZSIpKQpgYGAKCgoKCgo=